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

feat: add a simple sdk for golang #25

Merged
merged 4 commits into from
Dec 4, 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
87 changes: 87 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,83 @@ sudo ./bin/bittwister start -d eth0 -l 100
sudo ./bin/bittwister start -d eth0 -j 10
```

### Start the API server

```bash
sudo ./bin/bittwister serve [flags]

Flags:
-h, --help help for serve
--log-level string log level (e.g. debug, info, warn, error, dpanic, panic, fatal) (default "info")
--origin-allowed string origin allowed for CORS (default "*")
--production-mode production mode (e.g. disable debug logs)
--serve-addr string address to serve on (default "localhost:9007")
```

### API Endpoints

Please note that all the endpoints have to be prefixed with `/api/v1`.

#### Packet Loss

- **Endpoint:** `/packetloss`
- `/start`
- **Method:** POST
- **Data**: `{"network_interface":"eth0","packet_loss_rate":30}`
- **Description:** Start packetloss service.
- `/status`
- **Method:** GET
- **Description:** Get packetloss status.
- `/stop`
- **Method:** POST
- **Description:** Stop packetloss service.

**example:**

```bash
curl -iX POST http://localhost:9007/api/v1/packetloss/start --data '{"network_interface":"eth0","packet_loss_rate":30}'
```

#### Bandwidth

- **Endpoint:** `/bandwidth`
- `/start`
- **Method:** POST
- **Data**: `{"network_interface":"eth0","bandwidth":1048576}`
- **Description:** Start bandwidth service.
- `/status`
- **Method:** GET
- **Description:** Get bandwidth status.
- `/stop`
- **Method:** POST
- **Description:** Stop bandwidth service.

#### Latency

- **Endpoint:** `/latency`
- `/start`
- **Method:** POST
- **Data**: `{"network_interface":"eth0","latency":100,"jitter":10}`
- **Description:** Start latency service.
- `/status`
- **Method:** GET
- **Description:** Get latency status.
- `/stop`
- **Method:** POST
- **Description:** Stop latency service.

#### Services

- **Endpoint:** `/services`
- `/status`
- **Method:** GET
- **Description:** Get all network restriction services statuses and their configured parameters.

### SDK for Go

The BitTwister SDK for Go provides a convenient interface to interact with the BitTwister tool, which applies network restrictions on a network interface, including bandwidth limitation, packet loss, latency, and jitter.
More details about the SDK and how to use it can be found [here](./sdk/README.md).

### Using Bittwister in Kubernetes

To utilize Bittwister within a Kubernetes environment, specific configurations must be added to the container.
Expand Down Expand Up @@ -75,6 +152,16 @@ The tests require docker to be installed. To run all the tests, execute the foll
make test
```

### Go unit tests

The Go unit tests can be run by executing the following command:

```bash
make test-go
```

**Note**: Root permission is required to run the unit tests. The tests are run on the loopback interface.

### Test Packet Loss

The packet loss function can be tested by running the following command:
Expand Down
70 changes: 70 additions & 0 deletions sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# BitTwister SDK for Go

The BitTwister SDK for Go provides a convenient interface to interact with the BitTwister tool, which applies network restrictions on a network interface, including bandwidth limitation, packet loss, latency, and jitter.

## Installation

To use this SDK, import it into your Go project:

```bash
go get -u github.com/celestiaorg/bittwister/sdk
```

## Usage

### Initialization

First, import the SDK into your Go project:

```go
import "github.com/celestiaorg/bittwister/sdk"
```

Next, create a client by specifying the base URL of where BitTwister is running e.g. _a container running on the same machine_:

```go
func main() {
baseURL := "http://localhost:9007/api/v1"
client := sdk.NewClient(baseURL)
// Use the client for API requests
}
```

### Examples

Bandwidth Service

Start the Bandwidth service with a specified network interface and bandwidth limit:

```go
req := sdk.BandwidthStartRequest{
NetworkInterfaceName: "eth0",
Limit: 100,
}

err := client.BandwidthStart(req)
if err != nil {
// Handle error
}
```

Stop the Bandwidth service:

```go
err := client.BandwidthStop()
if err != nil {
// Handle error
}
```

Retrieve the status of the Bandwidth service:

```go
status, err := client.BandwidthStatus()
if err != nil {
// Handle error
}
// Use status for further processing
```

Similarly, you can use PacketlossStart, PacketlossStop, PacketlossStatus, LatencyStart, LatencyStop, LatencyStatus, and other functions provided by the SDK following similar usage patterns.
59 changes: 59 additions & 0 deletions sdk/apis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package sdk

import (
"encoding/json"

"github.com/celestiaorg/bittwister/api/v1"
)

func (c *Client) PacketlossStart(req api.PacketLossStartRequest) error {
_, err := c.postResource("/packetloss/start", req)
return err
}

func (c *Client) PacketlossStop() error {
_, err := c.postResource("/packetloss/stop", nil)
return err
}

func (c *Client) PacketlossStatus() (*api.MetaMessage, error) {
return c.getServiceStatus("/packetloss/status")
}

func (c *Client) BandwidthStart(req api.BandwidthStartRequest) error {
_, err := c.postResource("/bandwidth/start", req)
return err
}

func (c *Client) BandwidthStop() error {
_, err := c.postResource("/bandwidth/stop", nil)
return err
}

func (c *Client) BandwidthStatus() (*api.MetaMessage, error) {
return c.getServiceStatus("/bandwidth/status")
}

func (c *Client) LatencyStart(req api.LatencyStartRequest) error {
_, err := c.postResource("/latency/start", req)
return err
}

func (c *Client) LatencyStop() error {
_, err := c.postResource("/latency/stop", nil)
return err
}

func (c *Client) LatencyStatus() (*api.MetaMessage, error) {
return c.getServiceStatus("/latency/status")
}

func (c *Client) AllServicesStatus() ([]api.ServiceStatus, error) {
resp, err := c.getResource("/services/status")
msgs := []api.ServiceStatus{}

if err := json.Unmarshal(resp, &msgs); err != nil {
return nil, err
}
return msgs, err
}
82 changes: 82 additions & 0 deletions sdk/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package sdk

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"

"github.com/celestiaorg/bittwister/api/v1"
)

type Client struct {
baseURL string
httpClient *http.Client
}

func NewClient(baseURL string) *Client {
return &Client{
baseURL: baseURL,
httpClient: http.DefaultClient,
}
}

func (c *Client) getResource(resPath string) ([]byte, error) {
resp, err := c.httpClient.Get(c.baseURL + resPath)
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status: %d", resp.StatusCode)
}

body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

return body, nil
}

func (c *Client) postResource(resPath string, requestBody interface{}) ([]byte, error) {
requestBodyJSON, err := json.Marshal(requestBody)
if err != nil {
return nil, err
}

req, err := http.NewRequest("POST", c.baseURL+resPath, bytes.NewBuffer(requestBodyJSON))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")

resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
return nil, fmt.Errorf("unexpected status: %d", resp.StatusCode)
}

body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

return body, nil
}

func (c *Client) getServiceStatus(resPath string) (*api.MetaMessage, error) {
resp, err := c.getResource(resPath)
msg := &api.MetaMessage{}

if err := json.Unmarshal(resp, msg); err != nil {
return nil, err
}
return msg, err
}
Loading
Loading