From 7219cc2269903a629fd4d4db597b7e58a5376083 Mon Sep 17 00:00:00 2001 From: Redouane Lakrache Date: Thu, 15 Aug 2024 13:03:37 +0200 Subject: [PATCH] [E2E] Add simple rest service for e2e tests (#725) ## 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 ## 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. --- Tiltfile | 11 +++++++- config.yml | 15 ++++++++++ e2e/tests/init_test.go | 12 +++++++- e2e/tests/node.go | 29 ++++++++++++-------- e2e/tests/relay.feature | 14 ++++------ localnet/kubernetes/rest.yaml | 12 ++++++++ localnet/kubernetes/values-relayminer-1.yaml | 6 ++++ x/tokenomics/types/tx.pb.go | 1 + 8 files changed, 79 insertions(+), 21 deletions(-) create mode 100644 localnet/kubernetes/rest.yaml diff --git a/Tiltfile b/Tiltfile index e74a2c179..131ce8a75 100644 --- a/Tiltfile +++ b/Tiltfile @@ -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( @@ -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"]) \ No newline at end of file diff --git a/config.yml b/config.yml index 765970617..188eef958 100644 --- a/config.yml +++ b/config.yml @@ -160,6 +160,8 @@ genesis: service_configs: - service: id: anvil + - service: + id: rest - service: id: ollama stake: @@ -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: @@ -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" diff --git a/e2e/tests/init_test.go b/e2e/tests/init_test.go index 035c17b73..f204a4ad5 100644 --- a/e2e/tests/init_test.go +++ b/e2e/tests/init_test.go @@ -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 @@ -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)) } diff --git a/e2e/tests/node.go b/e2e/tests/node.go index 92fb497e8..0b6fae495 100644 --- a/e2e/tests/node.go +++ b/e2e/tests/node.go @@ -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 @@ -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 @@ -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...) } } @@ -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. @@ -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 diff --git a/e2e/tests/relay.feature b/e2e/tests/relay.feature index e93081ef7..21609d0cc 100644 --- a/e2e/tests/relay.feature +++ b/e2e/tests/relay.feature @@ -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 diff --git a/localnet/kubernetes/rest.yaml b/localnet/kubernetes/rest.yaml new file mode 100644 index 000000000..1a4a9c1fc --- /dev/null +++ b/localnet/kubernetes/rest.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: rest +spec: + selector: + app: rest + ports: + - protocol: TCP + name: rpc + port: 10000 + targetPort: 10000 diff --git a/localnet/kubernetes/values-relayminer-1.yaml b/localnet/kubernetes/values-relayminer-1.yaml index 18d1c759a..b17bd5765 100644 --- a/localnet/kubernetes/values-relayminer-1.yaml +++ b/localnet/kubernetes/values-relayminer-1.yaml @@ -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 diff --git a/x/tokenomics/types/tx.pb.go b/x/tokenomics/types/tx.pb.go index 5bc7d33e4..91f06eeff 100644 --- a/x/tokenomics/types/tx.pb.go +++ b/x/tokenomics/types/tx.pb.go @@ -133,6 +133,7 @@ type MsgUpdateParam struct { // specified in the `Params` message in `proof/params.proto.` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Types that are valid to be assigned to AsType: + // // *MsgUpdateParam_AsString // *MsgUpdateParam_AsInt64 // *MsgUpdateParam_AsBytes