Skip to content

Commit

Permalink
pandora scenario gun variables
Browse files Browse the repository at this point in the history
add support for "locals" block in hcl
0755b159bcce17c08dffde7f4441b9ec3b161244
  • Loading branch information
nickclmb committed Apr 24, 2024
1 parent b397f9b commit f54acd1
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 27 deletions.
115 changes: 110 additions & 5 deletions components/providers/scenario/config/hcl.go
Expand Up @@ -4,8 +4,13 @@ import (
"fmt"
"io"

"github.com/hashicorp/hcl/v2/hclsimple"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2/hclparse"
"github.com/spf13/afero"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/function"
"github.com/zclconf/go-cty/cty/function/stdlib"
)

type AmmoHCL struct {
Expand Down Expand Up @@ -91,14 +96,114 @@ type CallPreprocessorHCL struct {
func ParseHCLFile(file afero.File) (AmmoHCL, error) {
const op = "hcl.ParseHCLFile"

var config AmmoHCL
bytes, err := io.ReadAll(file)
if err != nil {
return AmmoHCL{}, fmt.Errorf("%s, io.ReadAll, %w", op, err)
}
err = hclsimple.Decode(file.Name(), bytes, nil, &config)
if err != nil {
return AmmoHCL{}, fmt.Errorf("%s, hclsimple.Decode, %w", op, err)

parser := hclparse.NewParser()
f, diag := parser.ParseHCL(bytes, file.Name())
if diag.HasErrors() {
return AmmoHCL{}, diag
}

localsBodyContent, remainingBody, diag := f.Body.PartialContent(localsSchema())
// diag still may have errors, because PartialContent doesn't know about Functions and self-references to locals
if localsBodyContent == nil || remainingBody == nil {
return AmmoHCL{}, diag
}

hclContext, diag := decodeLocals(localsBodyContent)
if diag.HasErrors() {
return AmmoHCL{}, diag
}

var config AmmoHCL
diag = gohcl.DecodeBody(remainingBody, hclContext, &config)
if diag.HasErrors() {
return AmmoHCL{}, diag
}
return config, nil
}

func decodeLocals(localsBodyContent *hcl.BodyContent) (*hcl.EvalContext, hcl.Diagnostics) {
vars := map[string]cty.Value{}
hclContext := buildHclContext(vars)
for _, block := range localsBodyContent.Blocks {
if block == nil {
continue
}
if block.Type == "locals" {
newVars, err := decodeLocalBlock(block, hclContext)
if err != nil {
return nil, err
}
hclContext = buildHclContext(mergeMaps(vars, newVars))
}
}
return hclContext, nil
}

func mergeMaps[K comparable, V any](to, from map[K]V) map[K]V {
for k, v := range from {
to[k] = v
}
return to
}

func decodeLocalBlock(localsBlock *hcl.Block, hclContext *hcl.EvalContext) (map[string]cty.Value, hcl.Diagnostics) {
attrs, err := localsBlock.Body.JustAttributes()
if err != nil {
return nil, err
}

vars := map[string]cty.Value{}
for name, attr := range attrs {
val, err := attr.Expr.Value(hclContext)
if err != nil {
return nil, err
}
vars[name] = val
}

return vars, nil
}

func localsSchema() *hcl.BodySchema {
return &hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{
Type: "locals",
LabelNames: []string{},
},
},
}
}

func buildHclContext(vars map[string]cty.Value) *hcl.EvalContext {
return &hcl.EvalContext{
Variables: map[string]cty.Value{
"local": cty.ObjectVal(vars),
},
Functions: map[string]function.Function{
// collection functions
"coalesce": stdlib.CoalesceFunc,
"coalescelist": stdlib.CoalesceListFunc,
"compact": stdlib.CompactFunc,
"concat": stdlib.ConcatFunc,
"distinct": stdlib.DistinctFunc,
"element": stdlib.ElementFunc,
"flatten": stdlib.FlattenFunc,
"index": stdlib.IndexFunc,
"keys": stdlib.KeysFunc,
"lookup": stdlib.LookupFunc,
"merge": stdlib.MergeFunc,
"reverse": stdlib.ReverseListFunc,
"slice": stdlib.SliceFunc,
"sort": stdlib.SortFunc,
"split": stdlib.SplitFunc,
"values": stdlib.ValuesFunc,
"zipmap": stdlib.ZipmapFunc,
},
}
}
33 changes: 18 additions & 15 deletions components/providers/scenario/testdata/http_payload.hcl
@@ -1,4 +1,16 @@

locals {
common_headers = {
Content-Type = "application/json"
Useragent = "Yandex"
}
next = "next"
}
locals {
auth_headers = merge(local.common_headers, {
Authorization = "Bearer {{.request.auth_req.postprocessor.token}}"
})
next = "next"
}
variable_source "users" "file/csv" {
file = "testdata/users.csv"
fields = ["user_id", "name", "pass"]
Expand All @@ -17,10 +29,7 @@ variable_source "variables" "variables" {
request "auth_req" {
method = "POST"
uri = "/auth"
headers = {
Content-Type = "application/json"
Useragent = "Yandex"
}
headers = local.common_headers
tag = "auth"
body = <<EOF
{"user_id": {{.request.auth_req.preprocessor.user_id}}}
Expand All @@ -31,7 +40,7 @@ EOF

preprocessor {
mapping = {
user_id = "source.users[next].user_id"
user_id = "source.users[${local.next}].user_id"
}
}
postprocessor "var/header" {
Expand Down Expand Up @@ -61,11 +70,9 @@ EOF
}
request "list_req" {
method = "GET"
headers = {
headers = merge(local.common_headers, {
Authorization = "Bearer {{.request.auth_req.postprocessor.token}}"
Content-Type = "application/json"
Useragent = "Yandex"
}
})
tag = "list"
uri = "/list"

Expand All @@ -79,11 +86,7 @@ request "list_req" {
request "order_req" {
method = "POST"
uri = "/order"
headers = {
Authorization = "Bearer {{.request.auth_req.postprocessor.token}}"
Content-Type = "application/json"
Useragent = "Yandex"
}
headers = local.auth_headers
tag = "order_req"
body = <<EOF
{"item_id": {{.request.order_req.preprocessor.item}}}
Expand Down
11 changes: 4 additions & 7 deletions components/providers/scenario/testdata/http_payload.yaml
Expand Up @@ -20,7 +20,7 @@ requests:
- name: auth_req
method: POST
uri: /auth
headers:
headers: &global-headers
Content-Type: application/json
Useragent: Yandex
tag: auth
Expand Down Expand Up @@ -53,10 +53,9 @@ requests:
- name: list_req
method: GET
uri: /list
headers:
headers: &auth-headers
<<: *global-headers
Authorization: Bearer {{.request.auth_req.postprocessor.token}}
Content-Type: application/json
Useragent: Yandex
tag: list
postprocessors:
- type: var/jsonpath
Expand All @@ -67,9 +66,7 @@ requests:
method: POST
uri: /order
headers:
Authorization: Bearer {{.request.auth_req.postprocessor.token}}
Content-Type: application/json
Useragent: Yandex
<<: *auth-headers
tag: order_req
body: |
{"item_id": {{.request.order_req.preprocessor.item}}}
Expand Down

0 comments on commit f54acd1

Please sign in to comment.