Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CI: Fast multidimensional Interop tests #1991

Merged
merged 8 commits into from
Jan 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 32 additions & 19 deletions .github/workflows/interop-test.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
on: [push, pull_request]
name: Interoperability Testing
on:
pull_request:
push:
branches:
- "master"

jobs:
# NOTE: during a pull request run, github creates a merge commit referenced in `github.sha`
# that merge commit is not a regular commit. You won't find it with a regular `git checkout SHA` and
# tools like `go get repo@SHA` won't find it.
#
# As a workaround, we generate a path to the actual pull request's commit, it looks like:
# `github.com/external-org/go-libp2p@latest-commit-on-their-branch`
run-ping-interop-cross-version:
uses: "libp2p/test-plans/.github/workflows/run-composition.yml@master"
build-ping-container:
name: Build Ping interop container
marten-seemann marked this conversation as resolved.
Show resolved Hide resolved
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Build image
marten-seemann marked this conversation as resolved.
Show resolved Hide resolved
working-directory: ./test-plans
run: make
- name: Upload ping versions info
uses: actions/upload-artifact@v3
with:
name: ping-versions
path: ./test-plans/ping-versions.json
- name: Upload image tar
uses: actions/upload-artifact@v3
with:
name: ping-image
path: ./test-plans/ping-image.tar
run-multidim-interop:
needs: build-ping-container
uses: "libp2p/test-plans/.github/workflows/run-testplans.yml@master"
with:
composition_file: "ping/_compositions/go-cross-versions.toml"
custom_git_target: github.com/${{ github.event.pull_request.head.repo.full_name || github.event.repository.full_name }}
custom_git_reference: ${{ github.event.pull_request.head.sha || github.sha }}
run-ping-interop-cross-implementation:
uses: "libp2p/test-plans/.github/workflows/run-composition.yml@master"
with:
composition_file: "ping/_compositions/all-interop-latest.toml"
custom_git_target: github.com/${{ github.event.pull_request.head.repo.full_name || github.event.repository.full_name }}
custom_git_reference: ${{ github.event.pull_request.head.sha || github.sha }}
custom_interop_target: go
dir: "multidim-interop"
extra-versions: ping-versions
image-tar: ping-image
test-filter: "go-libp2p-head"
47 changes: 0 additions & 47 deletions .github/workflows/interop.yml

This file was deleted.

2 changes: 2 additions & 0 deletions test-plans/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ping-image.tar
ping-image.json
10 changes: 10 additions & 0 deletions test-plans/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
all: ping-image.tar

ping-image.tar: PingDockerfile go.mod go.sum cmd/ping/
cd .. && docker build -t go-libp2p-head -f test-plans/PingDockerfile .
MarcoPolo marked this conversation as resolved.
Show resolved Hide resolved
docker image save -o $@ go-libp2p-head

.PHONY: clean

clean:
rm ping-image.tar
17 changes: 17 additions & 0 deletions test-plans/PingDockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# syntax=docker/dockerfile:1
# This is run from the parent directory to copy the whole go-libp2p codebase

FROM golang:1.19-alpine AS builder

WORKDIR /app/

COPY ./ .
WORKDIR /app/test-plans
RUN go mod download
RUN go build -o /testplan ./cmd/ping

FROM alpine
WORKDIR /app

COPY --from=builder /testplan /testplan
ENTRYPOINT [ "/testplan"]
38 changes: 38 additions & 0 deletions test-plans/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# test-plans test implementation

This folder defines the implementation for the test-plans interop tests.

# Running this test locally

You can run this test locally by having a local Redis instance and by having
another peer that this test can dial or listen for. For example to test that we
can dial/listen for ourselves we can do the following:

1. Start redis (needed by the tests): `docker run --rm -it -p 6379:6379
redis/redis-stack`.
2. In one terminal run the dialer: `REDIS_ADDR=localhost:6379 ip="0.0.0.0"
transport=quic-v1 security=quic muxer=quic is_dialer="true" go run
./cmd/ping`
3. In another terminal, run the listener: `REDIS_ADDR=localhost:6379
ip="0.0.0.0" transport=quic-v1 security=quic muxer=quic is_dialer="false" go
run ./cmd/ping`


To test the interop with other versions do something similar, except replace one
of these nodes with the other version's interop test.

# Running all interop tests locally with Compose

To run this test against all released libp2p versions you'll need to have the
(libp2p/test-plans)[https://github.com/libp2p/test-plans] checked out. Then do
the following:

1. Build the image: `make`.
2. Build the images for all released versions in `libp2p/test-plans`: `(cd <path
to >/libp2p/test-plans/multidim-interop/ && make)`.
3. Make a folder for the specified extra versions: `mkdir extra-versions && mv ping-versions.json extra-versions`
4. Run the test:
```
GO_LIBP2P_TEST_PLANS="$PWD"; (cd <path to >/libp2p/test-plans/multidim-interop/ && npm run test -- --extra-versions-dir=$GO_LIBP2P_TEST_PLANS/extra-versions --name-filter="go-libp2p-head")

```
152 changes: 152 additions & 0 deletions test-plans/cmd/ping/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package main

import (
"context"
"fmt"
"log"
"os"
"time"

"github.com/go-redis/redis/v8"
"github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/p2p/muxer/mplex"
"github.com/libp2p/go-libp2p/p2p/muxer/yamux"
"github.com/libp2p/go-libp2p/p2p/protocol/ping"
noise "github.com/libp2p/go-libp2p/p2p/security/noise"
libp2ptls "github.com/libp2p/go-libp2p/p2p/security/tls"
libp2pquic "github.com/libp2p/go-libp2p/p2p/transport/quic"
"github.com/libp2p/go-libp2p/p2p/transport/tcp"
"github.com/libp2p/go-libp2p/p2p/transport/websocket"
libp2pwebtransport "github.com/libp2p/go-libp2p/p2p/transport/webtransport"
ma "github.com/multiformats/go-multiaddr"
)

func main() {
var (
transport = os.Getenv("transport")
secureChannel = os.Getenv("security")
muxer = os.Getenv("muxer")
isDialerStr = os.Getenv("is_dialer")
ip = os.Getenv("ip")
redisAddr = os.Getenv("REDIS_ADDR")
)

if redisAddr == "" {
redisAddr = "redis:6379"
}

ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()

// Get peer information via redis
rClient := redis.NewClient(&redis.Options{
DialTimeout: 10 * time.Second,
Addr: redisAddr,
Password: "",
DB: 0,
})
defer rClient.Close()

// Make sure redis is ready
_, err := rClient.Ping(ctx).Result()
if err != nil {
log.Fatalf("Failed to connect to redis: %s", err)
}

isDialer := isDialerStr == "true"

options := []libp2p.Option{}

var listenAddr string
switch transport {
case "ws":
options = append(options, libp2p.Transport(websocket.New))
listenAddr = fmt.Sprintf("/ip4/%s/tcp/0/ws", ip)
case "tcp":
options = append(options, libp2p.Transport(tcp.NewTCPTransport))
listenAddr = fmt.Sprintf("/ip4/%s/tcp/0", ip)
case "quic":
options = append(options, libp2p.Transport(libp2pquic.NewTransport))
listenAddr = fmt.Sprintf("/ip4/%s/udp/0/quic", ip)
case "quic-v1":
options = append(options, libp2p.Transport(libp2pquic.NewTransport))
listenAddr = fmt.Sprintf("/ip4/%s/udp/0/quic-v1", ip)
case "webtransport":
options = append(options, libp2p.Transport(libp2pwebtransport.New))
listenAddr = fmt.Sprintf("/ip4/%s/udp/0/quic-v1/webtransport", ip)
default:
log.Fatalf("Unsupported transport: %s", transport)
}
options = append(options, libp2p.ListenAddrStrings(listenAddr))

switch secureChannel {
case "tls":
options = append(options, libp2p.Security(libp2ptls.ID, libp2ptls.New))
case "noise":
options = append(options, libp2p.Security(noise.ID, noise.New))
case "quic":
default:
log.Fatalf("Unsupported secure channel: %s", secureChannel)
}

switch muxer {
case "yamux":
options = append(options, libp2p.Muxer("/yamux/1.0.0", yamux.DefaultTransport))
case "mplex":
options = append(options, libp2p.Muxer("/mplex/6.7.0", mplex.DefaultTransport))
case "quic":
default:
log.Fatalf("Unsupported muxer: %s", muxer)
}

host, err := libp2p.New(options...)

if err != nil {
log.Fatalf("failed to instantiate libp2p instance: %s", err)
}
defer host.Close()

fmt.Println("My multiaddr is: ", host.Addrs())

if isDialer {
val, err := rClient.BLPop(ctx, 20*time.Second, "listenerAddr").Result()
if err != nil {
log.Fatal("Failed to wait for listener to be ready")
}
otherMa := ma.StringCast(val[1])
fmt.Println("Other peer multiaddr is: ", otherMa)
otherMa, p2pComponent := ma.SplitLast(otherMa)
otherPeerId, err := peer.Decode(p2pComponent.Value())
if err != nil {
log.Fatal("Failed to get peer id from multiaddr")
}
err = host.Connect(ctx, peer.AddrInfo{
ID: otherPeerId,
Addrs: []ma.Multiaddr{otherMa},
})
if err != nil {
log.Fatal("Failed to connect to other peer")
}

ping := ping.NewPingService(host)

res := <-ping.Ping(ctx, otherPeerId)
if res.Error != nil {
log.Fatal(res.Error)
}

fmt.Println("Ping successful: ", res.RTT)

rClient.RPush(ctx, "dialerDone", "").Result()
} else {
_, err := rClient.RPush(ctx, "listenerAddr", host.Addrs()[0].Encapsulate(ma.StringCast("/p2p/"+host.ID().String())).String()).Result()
if err != nil {
log.Fatal("Failed to send listener address")
}
_, err = rClient.BLPop(ctx, 20*time.Second, "dialerDone").Result()
if err != nil {
log.Fatal("Failed to wait for dialer conclusion")
}
}
}
Loading