Skip to content

Commit

Permalink
🚧 Add golang client API with test suite
Browse files Browse the repository at this point in the history
  • Loading branch information
mudler committed Nov 26, 2021
1 parent 14185a6 commit 1826d5a
Show file tree
Hide file tree
Showing 8 changed files with 300 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .github/servicestest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
if [ $1 == "expose" ]; then
./edgevpn service-add testservice 127.0.0.1:8080 &

((count = 100))
((count = 240))
while [[ $count -ne 0 ]] ; do
sleep 2
curl http://localhost:8080/api/ledger/tests/services | grep "doneservice"
Expand Down
10 changes: 10 additions & 0 deletions .github/tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash

set -ex

./edgevpn api &

GO111MODULE=off go get github.com/onsi/ginkgo/ginkgo
GO111MODULE=off go get github.com/onsi/gomega/...

TEST_INSTANCE="http://localhost:8080" ginkgo -r api/client
29 changes: 28 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,33 @@ jobs:
path: edgevpn
if-no-files-found: error

test-suite:
runs-on: ubuntu-latest
needs: build
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.16
- name: Download result for build
uses: actions/download-artifact@v2
with:
name: connection
path: ./
- name: Download result for build
uses: actions/download-artifact@v2
with:
name: edgevpn
path: ./
- name: Test suite
run: |
chmod +x edgevpn
EDGEVPNCONFIG=config.yaml ./.github/tests.sh
vpntest:
runs-on: ubuntu-latest
needs: build
Expand Down Expand Up @@ -66,7 +93,7 @@ jobs:
- name: Ping test
run: |
chmod +x edgevpn
sudo EDGEVPNCONFIG=config.yaml IFACE=edgevpn0 ADDRESS=${{ matrix.ip }} ./edgevpn &
sudo EDGEVPNCONFIG=config.yaml IFACE=edgevpn0 ADDRESS=${{ matrix.ip }} ./edgevpn --api &
bash ./.github/vpntest.sh ${{ matrix.target_ip }}
servicestest:
Expand Down
148 changes: 145 additions & 3 deletions api/client/client.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package client

import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"

"github.com/mudler/edgevpn/pkg/blockchain"
"github.com/mudler/edgevpn/pkg/edgevpn/types"
)

Expand Down Expand Up @@ -60,7 +62,8 @@ func NewClient(o ...Option) *Client {
}

func (c *Client) do(method, endpoint string, params map[string]string) (*http.Response, error) {
baseURL := fmt.Sprintf("%s/%s", c.host, endpoint)
baseURL := fmt.Sprintf("%s%s", c.host, endpoint)

req, err := http.NewRequest(method, baseURL, nil)
if err != nil {
return nil, err
Expand Down Expand Up @@ -123,7 +126,7 @@ func (c *Client) Users() (data []types.User, err error) {
return
}

func (c *Client) Ledger() (data map[string]string, err error) {
func (c *Client) Ledger() (data map[string]map[string]blockchain.Data, err error) {
res, err := c.do(http.MethodGet, ledgerURL, nil)
if err != nil {
return
Expand All @@ -139,7 +142,7 @@ func (c *Client) Ledger() (data map[string]string, err error) {
return
}

func (c *Client) Blockchain() (data []map[string]string, err error) {
func (c *Client) Blockchain() (data blockchain.Block, err error) {
res, err := c.do(http.MethodGet, blockchainURL, nil)
if err != nil {
return
Expand Down Expand Up @@ -170,3 +173,142 @@ func (c *Client) Machines() (resp []types.Machine, err error) {
}
return
}

func (c *Client) GetBucket(b string) (resp map[string]blockchain.Data, err error) {
res, err := c.do(http.MethodGet, fmt.Sprintf("%s/%s", ledgerURL, b), nil)
if err != nil {
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return resp, err
}
if err = json.Unmarshal(body, &resp); err != nil {
return resp, err
}
return
}

func (c *Client) GetBucketKeys(b string) (resp []string, err error) {
d, err := c.GetBucket(b)
if err != nil {
return resp, err
}
for k := range d {
resp = append(resp, k)
}
return
}

func (c *Client) GetBuckets() (resp []string, err error) {
d, err := c.Ledger()
if err != nil {
return resp, err
}
for k := range d {
resp = append(resp, k)
}
return
}

func (c *Client) GetBucketKey(b, k string) (resp blockchain.Data, err error) {
res, err := c.do(http.MethodGet, fmt.Sprintf("%s/%s/%s", ledgerURL, b, k), nil)
if err != nil {
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return resp, err
}

var r string
if err = json.Unmarshal(body, &r); err != nil {
return resp, err
}

if err = json.Unmarshal([]byte(r), &r); err != nil {
return resp, err
}

d, err := base64.URLEncoding.DecodeString(r)
if err != nil {
return resp, err
}
resp = blockchain.Data(string(d))
return
}

func (c *Client) Put(b, k string, v interface{}) (err error) {
s := struct{ State string }{}

dat, err := json.Marshal(v)
if err != nil {
return
}

d := base64.URLEncoding.EncodeToString(dat)

res, err := c.do(http.MethodPut, fmt.Sprintf("%s/%s/%s/%s", ledgerURL, b, k, d), nil)
if err != nil {
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
}

if err = json.Unmarshal(body, &s); err != nil {
return err
}

if s.State != "Announcing" {
return fmt.Errorf("unexpected state '%s'", s.State)
}

return
}

func (c *Client) Delete(b, k string) (err error) {
s := struct{ State string }{}
res, err := c.do(http.MethodDelete, fmt.Sprintf("%s/%s/%s", ledgerURL, b, k), nil)
if err != nil {
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
}
if err = json.Unmarshal(body, &s); err != nil {
return err
}
if s.State != "Announcing" {
return fmt.Errorf("unexpected state '%s'", s.State)
}

return
}

func (c *Client) DeleteBucket(b string) (err error) {
s := struct{ State string }{}
res, err := c.do(http.MethodDelete, fmt.Sprintf("%s/%s", ledgerURL, b), nil)
if err != nil {
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
}
if err = json.Unmarshal(body, &s); err != nil {
return err
}
if s.State != "Announcing" {
return fmt.Errorf("unexpected state '%s'", s.State)
}

return
}
19 changes: 19 additions & 0 deletions api/client/client_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package client_test

import (
"fmt"
"os"
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func TestClient(t *testing.T) {
if testInstance == "" {
fmt.Println("a testing instance has to be defined with TEST_INSTANCE")
os.Exit(1)
}
RegisterFailHandler(Fail)
RunSpecs(t, "Client Suite")
}
88 changes: 88 additions & 0 deletions api/client/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package client_test

import (
"math/rand"
"os"
"time"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"

. "github.com/mudler/edgevpn/api/client"
)

var testInstance = os.Getenv("TEST_INSTANCE")

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

func randStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}

var _ = Describe("Client", func() {
c := NewClient(WithHost(testInstance))

// Start the test suite only if we have some machines connected
BeforeSuite(func() {
Eventually(func() (int, error) {
m, err := c.Machines()
return len(m), err
}, 100*time.Second, 1*time.Second).Should(BeNumerically(">=", 0))
})

Context("Operates blockchain", func() {
var testBucket string

AfterEach(func() {
Eventually(c.GetBuckets, 100*time.Second, 1*time.Second).Should(ContainElement(testBucket))
err := c.DeleteBucket(testBucket)
Expect(err).ToNot(HaveOccurred())
Eventually(c.GetBuckets, 100*time.Second, 1*time.Second).ShouldNot(ContainElement(testBucket))
})

BeforeEach(func() {
testBucket = randStringBytes(10)
})

It("Puts string data", func() {
err := c.Put(testBucket, "foo", "bar")
Expect(err).ToNot(HaveOccurred())

Eventually(c.GetBuckets, 100*time.Second, 1*time.Second).Should(ContainElement(testBucket))
Eventually(func() ([]string, error) { return c.GetBucketKeys(testBucket) }, 100*time.Second, 1*time.Second).Should(ContainElement("foo"))

Eventually(func() (string, error) {
resp, err := c.GetBucketKey(testBucket, "foo")
if err == nil {
var r string
resp.Unmarshal(&r)
return r, nil
}
return "", err
}, 100*time.Second, 1*time.Second).Should(Equal("bar"))

m, err := c.Ledger()
Expect(err).ToNot(HaveOccurred())
Expect(len(m) > 0).To(BeTrue())
})

It("Puts random data", func() {
err := c.Put(testBucket, "foo2", struct{ Foo string }{Foo: "bar"})
Expect(err).ToNot(HaveOccurred())
Eventually(func() (string, error) {
resp, err := c.GetBucketKey(testBucket, "foo2")
if err == nil {
var r struct{ Foo string }
resp.Unmarshal(&r)
return r.Foo, nil
}

return "", err
}, 100*time.Second, 1*time.Second).Should(Equal("bar"))
})
})
})
17 changes: 8 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,30 @@ go 1.16
require (
github.com/gookit/color v1.5.0 // indirect
github.com/ipfs/go-ipns v0.1.2 // indirect
github.com/ipfs/go-log v1.0.5 // indirect
github.com/ipfs/go-log/v2 v2.3.0
github.com/ipfs/go-log v1.0.5
github.com/kr/text v0.2.0 // indirect
github.com/labstack/echo/v4 v4.6.1 // indirect
github.com/labstack/echo/v4 v4.6.1
github.com/libp2p/go-libp2p v0.15.1
github.com/libp2p/go-libp2p-core v0.9.0
github.com/libp2p/go-libp2p-discovery v0.5.1
github.com/libp2p/go-libp2p-kad-dht v0.12.1
github.com/libp2p/go-libp2p-pubsub v0.5.4
github.com/libp2p/go-libp2p-quic-transport v0.12.0 // indirect
github.com/lthibault/jitterbug v2.0.0+incompatible
github.com/mudler/go-isterminal v0.0.0-20211031135732-5e4e06fc5a58 // indirect
github.com/mudler/go-isterminal v0.0.0-20211031135732-5e4e06fc5a58
github.com/multiformats/go-multiaddr v0.4.0
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/onsi/ginkgo v1.16.4
github.com/onsi/gomega v1.13.0
github.com/peterbourgon/diskv v2.0.1+incompatible
github.com/pkg/errors v0.9.1
github.com/pterm/pterm v0.12.33 // indirect
github.com/pterm/pterm v0.12.33
github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
github.com/urfave/cli v1.22.5 // indirect
github.com/urfave/cli v1.22.5
github.com/vishvananda/netlink v1.1.0
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119
go.opencensus.io v0.23.0 // indirect
go.uber.org/zap v1.19.0
golang.org/x/net v0.0.0-20210913180222-943fd674d43e
golang.org/x/sys v0.0.0-20211031064116-611d5d643895 // indirect
gopkg.in/yaml.v2 v2.4.0
)

Expand Down
Loading

0 comments on commit 1826d5a

Please sign in to comment.