Skip to content

Commit

Permalink
Merge branch 'master' into feat/add-goreleaer-sentinel-and-directory-…
Browse files Browse the repository at this point in the history
…service
  • Loading branch information
shreyasbhat0 committed Nov 22, 2024
2 parents 3229665 + a584e3d commit edf8618
Show file tree
Hide file tree
Showing 16 changed files with 2,344 additions and 64 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ Contains all the PRs that improved the code without changing the behaviors.

## Added
- Added sentinel setup docs
- Added sentinel regression test
- Added go releaser for sentinel and directory service


## Changed
- Updated sentinel to handle provider events

Expand Down
221 changes: 220 additions & 1 deletion test/regression/cmd/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ func NewOperation(opMap map[string]any) Operation {
op = &OpState{}
case "check":
op = &OpCheck{}
case "check-repeated":
op = &OpCheckRepeated{}
case "check-websocket":
op = &OpCheckWebsocket{}
case "create-blocks":
Expand Down Expand Up @@ -93,7 +95,7 @@ func NewOperation(opMap map[string]any) Operation {

switch op.(type) {
// internal types have MarshalJSON methods necessary to decode
case *OpCheck, *OpTxSend, *OpTxBondProvider, *OpTxModProvider, *OpTxOpenContract, *OpTxCloseContract, *OpTxClaimContract:
case *OpCheck, *OpCheckRepeated, *OpTxSend, *OpTxBondProvider, *OpTxModProvider, *OpTxOpenContract, *OpTxCloseContract, *OpTxClaimContract:
// encode as json
buf := bytes.NewBuffer(nil)
enc := json.NewEncoder(buf)
Expand Down Expand Up @@ -245,6 +247,24 @@ type OpCheck struct {
Asserts []string `json:"asserts"`
}

type OpCheckRepeated struct {
OpBase `yaml:",inline"`
Description string `json:"description"`
Endpoint string `json:"endpoint"`
Method string `json:"method"`
Body string `json:"body"`
Params map[string]string `json:"params"`
Headers map[string]string `json:"headers"`
ArkAuth map[string]string `json:"arkauth"`
ContractAuth map[string]string `json:"contractauth"`
Status int `json:"status"`
AssertHeaders map[string]string `json:"assert_headers"`
InnerAssertHeaders map[string]string `json:"inner_assert_headers"`
Asserts []string `json:"asserts"`
InnerAsserts []string `json:"inner_asserts"`
Repeat int `json:"repeat"`
}

func createContractAuth(input map[string]string) (string, bool, error) {
///////// create contract auth signature //////////////
if len(input) == 0 {
Expand Down Expand Up @@ -467,6 +487,205 @@ func (op *OpCheck) Execute(_ *os.Process, logs chan string) error {
return nil
}

func (op *OpCheckRepeated) Execute(_ *os.Process, logs chan string) error {
// Abort if no endpoint is set (empty check op is allowed for breakpoint convenience)
if op.Endpoint == "" {
return fmt.Errorf("check")
}

// Set default method to GET if not specified
if op.Method == "" {
op.Method = http.MethodGet
}

// Determine the number of times to repeat the request
repeatCount := 1
if op.Repeat > 0 {
repeatCount = op.Repeat
}

var lastResp *http.Response
var lastBuf []byte

for i := 0; i < repeatCount; i++ {
// Update nonce in ArkAuth for each iteration
if len(op.ArkAuth) != 0 {
initialNonce, err := strconv.ParseInt(op.ArkAuth["nonce"], 10, 64)
if err != nil {
return fmt.Errorf("invalid initial nonce: %s", err)
}
op.ArkAuth["nonce"] = strconv.FormatInt(initialNonce+int64(i), 10)
}

var body io.Reader
if len(op.Body) > 0 {
body = strings.NewReader(op.Body)
}

// Build the request
req, err := http.NewRequest(op.Method, op.Endpoint, body)
if err != nil {
log.Fatal().Err(err).Msg("failed to build request")
}

// Set headers
for k, v := range op.Headers {
req.Header.Add(k, v)
}

// Add auth headers with updated nonce
arkauth, authOK, err := createAuth(op.ArkAuth)
if err != nil {
return err
}
if authOK {
req.Header.Add(sentinel.QueryArkAuth, arkauth)
}
contractAuth, contractAuthOK, err := createContractAuth(op.ContractAuth)
if err != nil {
return err
}
if contractAuthOK {
req.Header.Add(sentinel.QueryContract, contractAuth)
}

// Add query parameters
q := req.URL.Query()
for k, v := range op.Params {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()

// Send the request
resp, err := httpClient.Do(req)
if err != nil {
log.Err(err).Msg("failed to send request")
return err
}

// Read response body
buf, err := io.ReadAll(resp.Body)
if err != nil {
log.Err(err).Msg("failed to read response")
return err
}
defer resp.Body.Close()

// Store the response and body from the last request for assertions
lastResp = resp
lastBuf = buf

// Check status code for each request
if op.Status == 0 {
op.Status = 200 // Default to 200 if not specified
}
if resp.StatusCode != op.Status {
return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}

// Inner Assertions on Intermediate Requests
if i < repeatCount-1 {
// Perform Inner Header assertions on each iteration except the last
for k, v := range op.InnerAssertHeaders {
if val, exists := resp.Header[k]; exists {
if !strings.EqualFold(val[0], v) {
return fmt.Errorf("inner header assertion failed on iteration %d: expected %s, got %s", i+1, v, val[0])
}
} else {
return fmt.Errorf("missing inner header %s on iteration %d", k, i+1)
}
}

// Perform Inner Response assertions on each iteration except the last
for _, a := range op.InnerAsserts {
tmpl := template.Must(template.Must(templates.Clone()).Parse(a))
expr := bytes.NewBuffer(nil)
err := tmpl.Execute(expr, nil)
if err != nil {
log.Fatal().Err(err).Msg("failed to render inner assert expression")
}
a = expr.String()

cmd := exec.Command("jq", "-e", a)
cmd.Stdin = bytes.NewReader(buf)
out, err := cmd.CombinedOutput()
if err != nil {
if cmd.ProcessState.ExitCode() == 1 {
// Log process output if assertion fails
fmt.Println(ColorPurple + "\nLogs:" + ColorReset)
dumpLogs(logs)
}

// Output for debugging
fmt.Println(ColorPurple + "\nOperation:" + ColorReset)
_ = yaml.NewEncoder(os.Stdout).Encode(op)
fmt.Println(ColorPurple + "\nFailed Inner Assert: " + ColorReset + expr.String())
fmt.Println(ColorPurple + "\nEndpoint Response:" + ColorReset)
fmt.Println(string(buf) + "\n")

// Log error details
if cmd.ProcessState.ExitCode() != 1 {
drainLogs(logs)
fmt.Println(ColorRed + string(out) + ColorReset)
}

return err
}
}
}
}

// Final Header Assertions on the Last Response
for k, v := range op.AssertHeaders {
if val, exists := lastResp.Header[k]; exists {
if !strings.EqualFold(val[0], v) {
return fmt.Errorf("final header assertion failed: expected %s, got %s", v, val[0])
}
} else {
return fmt.Errorf("missing final header %s", k)
}
}

// Final Response Assertions on the Last Response Body
for _, a := range op.Asserts {
tmpl := template.Must(template.Must(templates.Clone()).Parse(a))
expr := bytes.NewBuffer(nil)
err := tmpl.Execute(expr, nil)
if err != nil {
log.Fatal().Err(err).Msg("failed to render assert expression")
}
a = expr.String()

cmd := exec.Command("jq", "-e", a)
cmd.Stdin = bytes.NewReader(lastBuf)
out, err := cmd.CombinedOutput()
if err != nil {
if cmd.ProcessState.ExitCode() == 1 {
// Log process output if assertion fails
fmt.Println(ColorPurple + "\nLogs:" + ColorReset)
dumpLogs(logs)
}

// Output for debugging
fmt.Println(ColorPurple + "\nOperation:" + ColorReset)
_ = yaml.NewEncoder(os.Stdout).Encode(op)
fmt.Println(ColorPurple + "\nFailed Final Assert: " + ColorReset + expr.String())
fmt.Println(ColorPurple + "\nEndpoint Response:" + ColorReset)
fmt.Println(string(lastBuf) + "\n")

// Log error details
if cmd.ProcessState.ExitCode() != 1 {
drainLogs(logs)
fmt.Println(ColorRed + string(out) + ColorReset)
}

return err
}
}

return nil
}

////////////////////////////////////////////////////////////////////////////////////////
// OpCreateBlocks
////////////////////////////////////////////////////////////////////////////////////////
Expand Down
36 changes: 26 additions & 10 deletions test/regression/mnt/exports/suites_contracts_pay-as-you-go.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
{
"@type": "/cosmos.auth.v1beta1.ModuleAccount",
"base_account": {
"account_number": "5",
"account_number": "6",
"address": "tarkeo1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3e79s43",
"pub_key": null,
"sequence": "0"
Expand All @@ -97,7 +97,7 @@
{
"@type": "/cosmos.auth.v1beta1.ModuleAccount",
"base_account": {
"account_number": "6",
"account_number": "7",
"address": "tarkeo1tygms3xhhs3yv487phx3dw4a95jn7t7ld7epr9",
"pub_key": null,
"sequence": "0"
Expand All @@ -118,10 +118,17 @@
},
"sequence": "2"
},
{
"@type": "/cosmos.auth.v1beta1.BaseAccount",
"account_number": "3",
"address": "tarkeo1w8ng7dxc3dezqxk8jt3f3nvl4rcwf8ds43v0lr",
"pub_key": null,
"sequence": "0"
},
{
"@type": "/cosmos.auth.v1beta1.ModuleAccount",
"base_account": {
"account_number": "7",
"account_number": "8",
"address": "tarkeo10d07y265gmmuvt4z0w9aw880jnsr700jk8l664",
"pub_key": null,
"sequence": "0"
Expand All @@ -134,7 +141,7 @@
{
"@type": "/cosmos.auth.v1beta1.ModuleAccount",
"base_account": {
"account_number": "4",
"account_number": "5",
"address": "tarkeo1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8t6gr9e",
"pub_key": null,
"sequence": "0"
Expand All @@ -147,7 +154,7 @@
{
"@type": "/cosmos.auth.v1beta1.ModuleAccount",
"base_account": {
"account_number": "10",
"account_number": "11",
"address": "tarkeo1kz2dkl8zlxwte008astc5e65htrxdcse6x3h3h",
"pub_key": null,
"sequence": "0"
Expand All @@ -158,7 +165,7 @@
{
"@type": "/cosmos.auth.v1beta1.ModuleAccount",
"base_account": {
"account_number": "9",
"account_number": "10",
"address": "tarkeo1k8925g52vwe5jgfp4nqr7ljuhs7nzu2f8g0za5",
"pub_key": null,
"sequence": "0"
Expand All @@ -179,7 +186,7 @@
{
"@type": "/cosmos.auth.v1beta1.ModuleAccount",
"base_account": {
"account_number": "8",
"account_number": "9",
"address": "tarkeo1m3h30wlvsf8llruxtpukdvsy0km2kum8y5t8tx",
"pub_key": null,
"sequence": "0"
Expand All @@ -202,7 +209,7 @@
{
"@type": "/cosmos.auth.v1beta1.ModuleAccount",
"base_account": {
"account_number": "3",
"account_number": "4",
"address": "tarkeo17xpfvakm2amg962yls6f84z3kell8c5luu0l8m",
"pub_key": null,
"sequence": "0"
Expand Down Expand Up @@ -251,6 +258,15 @@
}
]
},
{
"address": "tarkeo1w8ng7dxc3dezqxk8jt3f3nvl4rcwf8ds43v0lr",
"coins": [
{
"amount": "100",
"denom": "uarkeo"
}
]
},
{
"address": "tarkeo1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8t6gr9e",
"coins": [
Expand Down Expand Up @@ -287,7 +303,7 @@
"send_enabled": [],
"supply": [
{
"amount": "52001016066162928",
"amount": "52001016066163028",
"denom": "uarkeo"
}
]
Expand Down Expand Up @@ -579,7 +595,7 @@
},
"mint": {
"minter": {
"annual_provisions": "6760124958900879.440734825593079684",
"annual_provisions": "6760124958900892.440721382662647884",
"inflation": "0.129999865570695682"
},
"params": {
Expand Down
Loading

0 comments on commit edf8618

Please sign in to comment.