Skip to content

Commit

Permalink
[E2E] Add simple rest service for e2e tests (#725)
Browse files Browse the repository at this point in the history
## Summary

This PR introduces a simple REST service to be used in end-to-end (E2E)
tests as a replacement for the LLM service.

It utilizes the
[davarski/go-rest-api-demo](https://github.com/adavarski/REST-go-k8s-example)
Docker image to provide this service.

- [ ] Add a follow-up PR to enable this service on CI

## Issue

Due to E2E test flakiness, mostly observed in CI, which is likely caused
by the resource-intensive LLM service, we decided to use a simple REST
service for testing this feature.

- #723

## Type of change

Select one or more:

- [ ] New feature, functionality or library
- [ ] Bug fix
- [x] Code health or cleanup
- [ ] Documentation
- [ ] Other (specify)

## Testing

**Documentation changes** (only if making doc changes)
- [ ] `make docusaurus_start`; only needed if you make doc changes

**Local Testing** (only if making code changes)
- [x] **Unit Tests**: `make go_develop_and_test`
- [x] **LocalNet E2E Tests**: `make test_e2e`
- See [quickstart
guide](https://dev.poktroll.com/developer_guide/quickstart) for
instructions

**PR Testing** (only if making code changes)
- [x] **DevNet E2E Tests**: Add the `devnet-test-e2e` label to the PR.
- **THIS IS VERY EXPENSIVE**, so only do it after all the reviews are
complete.
- Optionally run `make trigger_ci` if you want to re-trigger tests
without any code changes
- If tests fail, try re-running failed tests only using the GitHub UI as
shown
[here](https://github.com/pokt-network/poktroll/assets/1892194/607984e9-0615-4569-9452-4c730190c1d2)


## Sanity Checklist

- [x] I have tested my changes using the available tooling
- [ ] I have commented my code
- [x] I have performed a self-review of my own code; both comments &
source code
- [ ] I create and reference any new tickets, if applicable
- [ ] I have left TODOs throughout the codebase, if applicable


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit


- **New Features**
- Introduced a new REST service configuration, enhancing the app's
capability to interact through RESTful APIs.
- Added service and endpoint definitions for REST, supporting broader
functionality in communication.

- **Bug Fixes**
- Improved request handling logic in the testing suite to dynamically
set HTTP methods based on input.

- **Tests**
- Updated test scenarios to reflect changes in service type and request
paths, ensuring accurate testing parameters.
- Added convenience method in the test suite for easier testing of GET
requests without data.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
red-0ne authored Aug 15, 2024
1 parent e753f19 commit 7219cc2
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 21 deletions.
11 changes: 10 additions & 1 deletion Tiltfile
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,11 @@ WORKDIR /
)

# Run data nodes & validators
k8s_yaml(["localnet/kubernetes/anvil.yaml", "localnet/kubernetes/validator-volume.yaml"])
k8s_yaml([
"localnet/kubernetes/anvil.yaml",
"localnet/kubernetes/rest.yaml",
"localnet/kubernetes/validator-volume.yaml"
])

# Provision validator
helm_resource(
Expand Down Expand Up @@ -370,3 +374,8 @@ if localnet_config["ollama"]["enabled"]:
cmd=execute_in_pod("ollama", "ollama pull " + localnet_config["ollama"]["model"]),
resource_deps=["ollama"],
)

if localnet_config["rest"]["enabled"]:
print("REST enabled: " + str(localnet_config["rest"]["enabled"]))
deployment_create("rest", image="davarski/go-rest-api-demo")
k8s_resource("rest", labels=["data_nodes"], port_forwards=["10000"])
15 changes: 15 additions & 0 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ genesis:
service_configs:
- service:
id: anvil
- service:
id: rest
- service:
id: ollama
stake:
Expand All @@ -181,6 +183,15 @@ genesis:
rev_share:
- address: pokt19a3t4yunp0dlpfjrp7qwnzwlrzd5fzs2gjaaaj
rev_share_percentage: "100"
- service:
id: rest
endpoints:
- configs: []
rpc_type: REST
url: http://relayminer1:8545
rev_share:
- address: pokt19a3t4yunp0dlpfjrp7qwnzwlrzd5fzs2gjaaaj
rev_share_percentage: "100"
- service:
id: ollama
endpoints:
Expand Down Expand Up @@ -215,6 +226,10 @@ genesis:
name: "ollama"
compute_units_per_relay: 1
owner_address: pokt1mx0klkkrj6v3dw8gs4nzlq0cq8lsktmx35t03e
- id: rest
name: "rest"
compute_units_per_relay: 1
owner_address: pokt1mx0klkkrj6v3dw8gs4nzlq0cq8lsktmx35t03e
proof:
params:
proof_request_probability: "0.25"
Expand Down
12 changes: 11 additions & 1 deletion e2e/tests/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,13 @@ func (s *suite) TheApplicationSendsTheSupplierASuccessfulRequestForServiceWithPa
// that allows any application to send a relay in LocalNet and our E2E Tests.
require.Equal(s, "app1", appName, "TODO_HACK: The LocalNet AppGateServer is self_signing and only supports app1.")

res, err := s.pocketd.RunCurlWithRetry(appGateServerUrl, serviceId, path, requestData, 5)
method := "POST"
// If requestData is empty, assume a GET request
if requestData == "" {
method = "GET"
}

res, err := s.pocketd.RunCurlWithRetry(appGateServerUrl, serviceId, method, path, requestData, 5)
require.NoError(s, err, "error sending relay request from app %q to supplier %q for service %q", appName, supplierOperatorName, serviceId)

var jsonContent json.RawMessage
Expand All @@ -477,6 +483,10 @@ func (s *suite) TheApplicationSendsTheSupplierASuccessfulRequestForServiceWithPa
}
}

func (s *suite) TheApplicationSendsTheSupplierASuccessfulRequestForServiceWithPath(appName, supplierName, serviceId, path string) {
s.TheApplicationSendsTheSupplierASuccessfulRequestForServiceWithPathAndData(appName, supplierName, serviceId, path, "")
}

func (s *suite) AModuleEndBlockEventIsBroadcast(module, eventType string) {
s.waitForNewBlockEvent(newEventTypeMatchFn(module, eventType))
}
Expand Down
29 changes: 18 additions & 11 deletions e2e/tests/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type commandResult struct {
type PocketClient interface {
RunCommand(args ...string) (*commandResult, error)
RunCommandOnHost(rpcUrl string, args ...string) (*commandResult, error)
RunCurl(rpcUrl, service, path, data string, args ...string) (*commandResult, error)
RunCurl(rpcUrl, service, method, path, data string, args ...string) (*commandResult, error)
}

// Ensure that pocketdBin struct fulfills PocketClient
Expand Down Expand Up @@ -94,24 +94,24 @@ func (p *pocketdBin) RunCommandOnHostWithRetry(rpcUrl string, numRetries uint8,
}

// RunCurl runs a curl command on the local machine
func (p *pocketdBin) RunCurl(rpcUrl, service, path, data string, args ...string) (*commandResult, error) {
func (p *pocketdBin) RunCurl(rpcUrl, service, method, path, data string, args ...string) (*commandResult, error) {
if rpcUrl == "" {
rpcUrl = defaultAppGateServerURL
}
return p.runCurlPostCmd(rpcUrl, service, path, data, args...)
return p.runCurlCmd(rpcUrl, service, method, path, data, args...)
}

// RunCurlWithRetry runs a curl command on the local machine with multiple retries.
// It also accounts for an ephemeral error that may occur due to DNS resolution such as "no such host".
func (p *pocketdBin) RunCurlWithRetry(rpcUrl, service, path, data string, numRetries uint8, args ...string) (*commandResult, error) {
func (p *pocketdBin) RunCurlWithRetry(rpcUrl, service, method, path, data string, numRetries uint8, args ...string) (*commandResult, error) {
// No more retries left
if numRetries <= 0 {
return p.RunCurl(rpcUrl, service, path, data, args...)
return p.RunCurl(rpcUrl, service, method, path, data, args...)
}
// Run the curl command
res, err := p.RunCurl(rpcUrl, service, path, data, args...)
res, err := p.RunCurl(rpcUrl, service, method, path, data, args...)
if err != nil {
return p.RunCurlWithRetry(rpcUrl, service, path, data, numRetries-1, args...)
return p.RunCurlWithRetry(rpcUrl, service, method, path, data, numRetries-1, args...)
}

// TODO_HACK: This is a list of common flaky / ephemeral errors that can occur
Expand All @@ -124,7 +124,7 @@ func (p *pocketdBin) RunCurlWithRetry(rpcUrl, service, path, data string, numRet
if strings.Contains(res.Stdout, ephemeralError) {
fmt.Println("Retrying due to ephemeral error:", res.Stdout)
time.Sleep(10 * time.Millisecond)
return p.RunCurlWithRetry(rpcUrl, service, path, data, numRetries-1, args...)
return p.RunCurlWithRetry(rpcUrl, service, method, path, data, numRetries-1, args...)
}
}

Expand Down Expand Up @@ -165,7 +165,7 @@ func (p *pocketdBin) runPocketCmd(args ...string) (*commandResult, error) {
}

// runCurlPostCmd is a helper to run a command using the local pocketd binary with the flags provided
func (p *pocketdBin) runCurlPostCmd(rpcUrl, service, path, data string, args ...string) (*commandResult, error) {
func (p *pocketdBin) runCurlCmd(rpcUrl, service, method, path, data string, args ...string) (*commandResult, error) {
// Ensure that if a path is provided, it starts with a "/".
// This is required for RESTful APIs that use a path to identify resources.
// For JSON-RPC APIs, the resource path should be empty, so empty paths are allowed.
Expand All @@ -176,9 +176,16 @@ func (p *pocketdBin) runCurlPostCmd(rpcUrl, service, path, data string, args ...
base := []string{
"-v", // verbose output
"-sS", // silent with error
"-X", "POST", // HTTP method
"-X", method, // HTTP method
"-H", "Content-Type: application/json", // HTTP headers
"--data", data, urlStr, // POST data
urlStr,
}

if method == "POST" {
base = append(base, "--data", data)
} else if len(data) > 0 {
fmt.Println(fmt.Sprintf("WARN: data provided but not being included in the %s request", method))

}
args = append(base, args...)
commandStr := "curl " + strings.Join(args, " ") // Create a string representation of the command
Expand Down
14 changes: 6 additions & 8 deletions e2e/tests/relay.feature
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@ Feature: Relay Namespace
And the session for application "app1" and service "anvil" contains the supplier "supplier1"
Then the application "app1" sends the supplier "supplier1" a successful request for service "anvil" with path "" and data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'

# TODO(#727): Add this test back.
# Scenario: App can send a REST relay to Supplier
# Given the user has the pocketd binary installed
# And the application "app1" is staked for service "ollama"
# And the supplier "supplier1" is staked for service "ollama"
# And the session for application "app1" and service "ollama" contains the supplier "supplier1"
# When the application "app1" sends the supplier "supplier1" a successful request for service "ollama" with path "/api/chat" and data '{"model": "qwen:0.5b", "stream": false, "messages": [{"role": "user", "content":"count from 1 to 10"}]}'
# And a "tokenomics" module "ClaimSettled" end block event is broadcast
Scenario: App can send a REST relay to Supplier
Given the user has the pocketd binary installed
And the application "app1" is staked for service "rest"
And the supplier "supplier1" is staked for service "rest"
And the session for application "app1" and service "rest" contains the supplier "supplier1"
When the application "app1" sends the supplier "supplier1" a successful request for service "rest" with path "/quote"

# TODO_TEST(@Olshansk):
# - Successful relay through applicat's sovereign appgate server
Expand Down
12 changes: 12 additions & 0 deletions localnet/kubernetes/rest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: rest
spec:
selector:
app: rest
ports:
- protocol: TCP
name: rpc
port: 10000
targetPort: 10000
6 changes: 6 additions & 0 deletions localnet/kubernetes/values-relayminer-1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,9 @@ config:
backend_url: http://ollama:11434/
publicly_exposed_endpoints:
- relayminer1
- service_id: rest
listen_url: http://0.0.0.0:8545
service_config:
backend_url: http://rest:10000/
publicly_exposed_endpoints:
- relayminer1
1 change: 1 addition & 0 deletions x/tokenomics/types/tx.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 7219cc2

Please sign in to comment.