grafana / k6-jslib-aws

Javascript Library allowing to interact with AWS resources from k6 scripts

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

S3Client returns null when `K6_DISCARD_RESPONSE_BODIES` is set to true

oleiade opened this issue · comments

A user recently reported the following issue in the k6 open-source support forum:

K6 S3Client returns null with K6_DISCARD_RESPONSE_BODIES=true.
Use case:
I don’t want to discard the body of the S3 client response to create test data, but for real load test responses, just checking the HTTP status is sufficient.

We should run an investigation on this, and figure out if this is the intended behavior, or if we need to adjust the behavior of the jslib.

Thanks for surfacing this issue on GitHub! I ran into this problem and it caused me to spin for quite a while thinking that there was either a bug with the client or a problem with my AWS config.

I added the following code to my setup() method after confirming that the config seemed reasonable:

  const config = AWSConfig.fromEnvironment();
  const client = new S3Client(config);

  const buckets = client.listBuckets();
  console.log(buckets);

This ran without any issue, and resulted in the following log in the k6 output:

INFO[0000] []

I also tested the getObject() method, and confirmed that no data is returned when discardResponseBodies is true. From my perspective this definitely feels like a defect, since I need to pull data from s3 that will be consumed by VUs as my test is executed.

Thank you for creating the issue.
Report a code example where the problem occurs.

Things I need to prepare to reproduce:

data.json This is the test data stored in S3. actually larger size.
{
  "requests": [
    {
      "query": "/hoge"
    },
    {
      "query": "/fuga"
    }
  ]
}
script.js I set this in a ConfigMap in Kubernetes. Writing the whole ConfigMap feels redundant and informational, so I'll only share the script.js.
import http from 'k6/http';
import { AWSConfig, S3Client } from 'https://jslib.k6.io/aws/0.7.1/s3.js';

const awsConfig = new AWSConfig({
  region: `${__ENV.AWS_REGION}`,
  accessKeyId: `${__ENV.AWS_ACCESS_KEY_ID}`,
  secretAccessKey: `${__ENV.AWS_SECRET_ACCESS_KEY}`,
});

const s3 = new S3Client(awsConfig);

export let options = {
  stages: [
    { target: `${__ENV.TEST_TARGET}`, duration: `${__ENV.TEST_DURATION}` },
  ]
};

export function setup() {
  const object = s3.getObject(`${__ENV.S3_BUCKET_NAME_TEST_DATA}`, `${__ENV.S3_KEY_TEST_DATA}`);
  console.log(object); // I'm logging here because I want to clarify the problem.
  return { requests: JSON.parse(object.data).requests };
}

export default function (data) {
  const index = Math.floor(Math.random() * data.requests.length)
  const base_url = `${__ENV.BASE_URL}`
  const url = base_url + data.requests[index].query
  const result = http.get(url);
};
k6.yaml This may not be necessary for some people. I happened to be using K6 Operator. I'm just posting here with the intention of sharing how the environment variables are set in my repro environment.
---
apiVersion: k6.io/v1alpha1
kind: K6
metadata:
  name: api-test
  namespace: k6-operator-system
spec:
  parallelism: 1
  arguments: --out statsd --tag test_run_id=${run_test_id} --include-system-env-vars
  script:
    configMap:
      name: ${configmap_name}
      file: script.js
#  cleanup: "post"
  runner:
    nodeselector:
      hoge/node-type: default
    image: grafana/k6:0.44.0
    env:
      - name: K6_STATSD_ENABLE_TAGS
        value: "true"
      - name: K6_STATSD_ADDR
        value: datadog.kube-system:8125
      - name: AWS_REGION
        value: "${region}"
      - name: S3_BUCKET_NAME_TEST_DATA
        value: "${s3_bucket}"
      - name: S3_KEY_TEST_DATA
        value: ${s3_key}
      - name: TEST_TARGET
        value: "10"
      - name: TEST_DURATION
        value: "300s"
      - name: BASE_URL
        value: "https://some-site.com"
      - name: AWS_ACCESS_KEY_ID
        valueFrom:
          secretKeyRef:
            name: ${secret_name}
            key: AWS_ACCESS_KEY_ID
      - name: AWS_SECRET_ACCESS_KEY
        valueFrom:
          secretKeyRef:
            name: ${secret_name}
            key: AWS_SECRET_ACCESS_KEY
      - name: K6_DISCARD_RESPONSE_BODIES
        value: "true"

result:

K6 console.log

time="2023-05-10T07:28:46Z" level=info msg="{\"key\":\"data.json\",\"lastModified\":null,\"size\":40017,\"storageClass\":\"STANDARD\",\"data\":null}" source=console
     █ setup
     data_received..................: 47 kB 546 kB/s
     data_sent......................: 971 B 11 kB/s
     http_req_blocked...............: avg=16.01ms min=16.01ms med=16.01ms max=16.01ms p(90)=16.01ms p(95)=16.01ms
     http_req_connecting............: avg=1.64ms  min=1.64ms  med=1.64ms  max=1.64ms  p(90)=1.64ms  p(95)=1.64ms 
     http_req_duration..............: avg=67.44ms min=67.44ms med=67.44ms max=67.44ms p(90)=67.44ms p(95)=67.44ms
       { expected_response:true }...: avg=67.44ms min=67.44ms med=67.44ms max=67.44ms p(90)=67.44ms p(95)=67.44ms
     http_req_failed................: 0.00% ✓ 0         ✗ 1   
     http_req_receiving.............: avg=1.8ms   min=1.8ms   med=1.8ms   max=1.8ms   p(90)=1.8ms   p(95)=1.8ms  
     http_req_sending...............: avg=37.23µs min=37.23µs med=37.23µs max=37.23µs p(90)=37.23µs p(95)=37.23µs
     http_req_tls_handshaking.......: avg=7.49ms  min=7.49ms  med=7.49ms  max=7.49ms  p(90)=7.49ms  p(95)=7.49ms 
     http_req_waiting...............: avg=65.6ms  min=65.6ms  med=65.6ms  max=65.6ms  p(90)=65.6ms  p(95)=65.6ms 
     http_reqs......................: 1     11.689142/s
     iteration_duration.............: avg=84.37ms min=84.37ms med=84.37ms max=84.37ms p(90)=84.37ms p(95)=84.37ms
     vus............................: 0     min=0       max=0 
     vus_max........................: 10    min=10      max=10
time="2023-05-10T07:28:47Z" level=error msg="TypeError: Cannot read property 'requests' of undefined\n\tat setup (file:///test/script.js:21:45(24))\n" hint="script exception"

If I remove K6_DISCARD_RESPONSE_BODIES=true and run it, the data will be correctly retrieved from S3.

If I am missing any information, please let me know.

Thanks.

Thanks a lot for you input 🙇🏻

We have investigated the issue and found a workaround: to ignore the discardResponseBodies option in the context of our client classes, we need to make sure that whenever we use http.request, we pass the responseType: 'text' | 'binary' option as a parameter.

We're looking into the best ways to integrate that change in the library, and a fix should land with version 0.8.0 👍🏻

Gave this a stab, and couldn't get to a satisfying solution yet. Moving this to 0.9.0.