Skip to content

Conversation

@Kristina-Pianykh
Copy link
Contributor

@Kristina-Pianykh Kristina-Pianykh commented Nov 16, 2025

This PR introduces an option to disable the initializer phase in a TestRun, allowing users to skip creating the initializer Job.

Key Changes

  1. spec.initializer.disabled added to the CRD with the default False value

Why use disabled instead of enabled?
To preserve backward compatibility.
If we introduced enabled, omitting it would default to false and unintentionally disable the initializer for existing users. Using disabled avoids this problem: omitting the field preserves the current behavior.

  1. New field Disabled added to the Pod struct

The initializer, starter, and runner are all represented using the same Pod type. Adding the field directly to this type ensures the API remains backward-compatible.

Why store it on Pod instead of creating a new InitializerPod type?
I explored creating a dedicated struct to have the new field for the initializer only like so:

type InitializerPod struct {
	Disabled bool `json:"disabled,omitempty"`
	Pod      `json:",inline"`
}

but this approach introduced several issues:

  • The initializer would become a different Go type, requiring changes across API usage.
  • Existing client code instantiating a TestRun in Go would break since initializer would no longer be a simple Pod:
&v1alpha1.TestRun{
    Spec: v1alpha1.TestRunSpec{
        Initializer: v1alpha1.InitializerSpec{
            Pod: v1alpha1.Pod{
                Image: "grafana/k6:latest",
            },
            Disable: false,
        },
...
}

For these reasons, I considered extending the existing Pod type as the least disruptive option.

  1. Updated TestRun lifecycle when initializer is disabled

When the initializer is disabled, stage transitions skip "initialization". The lifecycle becomes:
"""initialized" → …

  1. New condition: InitializerSkipped

A new condition type is introduced to explicitly record whether the initializer was skipped. Both True and False states are used to differentiate intentional skipping from default behavior.

  1. CRD schema and generated documentation updated

e2e Test

I've tried the e2e setup following the instructions in e2e/README.md but the first test run gets stuck with no logs to troubleshoot. For this reason, I provide my own scripts to test:

Initializer skipped
#!/usr/bin/env bash

kind create cluster

# build docker image from the k6-operator directory, load into the cluster and deploy the controller
IMG_NAME=k6operator IMG_TAG=foo make docker-build
kind load docker-image k6operator:foo
IMG_NAME=k6operator IMG_TAG=foo make deploy

# create a minimal k6 script `test.js`
rm test.js
cat <<'EOF' > test.js
import http from 'k6/http';

export default function () {
  const url = 'https://quickpizza.grafana.com/api/users/token/login';
  const payload = JSON.stringify({
    username: 'default',
    password: '12345678',
  });

  const params = {
    headers: {
      'Content-Type': 'application/json',
    },
  };

  // send a post request and save response as a variable
  const res = http.post(url, payload, params);
  sleep(5);
}
EOF

# create a configmap from the k6 script
kubectl create configmap my-k6 --from-file test.js -o yaml --dry-run=client | kubectl apply -f -

rm testrun.yaml
cat <<'EOF' > testrun.yaml
apiVersion: k6.io/v1alpha1
kind: TestRun
metadata:
  name: my-k6-test
  namespace: default
spec:
  initializer:
    disabled: true
  parallelism: 1
  script:
    configMap:
      name: my-k6
      file: test.js
  quiet: "false"    # get full logs
EOF

# apply the testrun
kubectl apply -f testrun.yaml
Backward compatible initialization
#!/usr/bin/env bash

kind create cluster

# build docker image from the k6-operator directory, load into the cluster and deploy the controller
IMG_NAME=k6operator IMG_TAG=foo make docker-build
kind load docker-image k6operator:foo
IMG_NAME=k6operator IMG_TAG=foo make deploy

# create a minimal k6 script `test.js`
rm test.js
cat <<'EOF' > test.js
import http from 'k6/http';

export default function () {
  const url = 'https://quickpizza.grafana.com/api/users/token/login';
  const payload = JSON.stringify({
    username: 'default',
    password: '12345678',
  });

  const params = {
    headers: {
      'Content-Type': 'application/json',
    },
  };

  // send a post request and save response as a variable
  const res = http.post(url, payload, params);
  sleep(5);
}
EOF

# create a configmap from the k6 script
kubectl create configmap my-k6 --from-file test.js -o yaml --dry-run=client | kubectl apply -f -

rm testrun.yaml
cat <<'EOF' > testrun.yaml
apiVersion: k6.io/v1alpha1
kind: TestRun
metadata:
  name: my-k6-test
  namespace: default
spec:
  parallelism: 1
  script:
    configMap:
      name: my-k6
      file: test.js
  quiet: "false"    # get full logs
EOF

# apply the testrun
kubectl apply -f testrun.yaml

Resolves #657

@Kristina-Pianykh Kristina-Pianykh force-pushed the allow-disabling-initializer branch from 0b9c270 to e456acd Compare November 16, 2025 13:13
Copy link
Collaborator

@yorugac yorugac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Kristina-Pianykh!
Thanks for the PR and the detailed rationale behind implementation 🙌

I'm not clear about the embedded struct though:

The initializer would become a different Go type, requiring changes across API usage.
Existing client code instantiating a TestRun in Go would break since initializer would no longer be a simple Pod:

Breaking in which cases exactly? Did you mean potential users of Go api package?
TBH, adding disable fields to all Pods is not exactly nice either. People might start expecting that those fields mean something.

There are also a couple of logic corrections that I added in the comments; please see.

Perhaps, the most important thing is that e2e tests should continue to work for sure 😅 If they don't, that means the change is breaking. Unless it was an issue with setting them up in the first place?

)

const (
InitializerSkipped = "InitializerSkipped"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition is set but never checked: what is the point of it then?
PS I don't say yet that we definitely won't need it, but right now it doesn't look like it.


// Not a clout test and initializer is set to disabled
// -> skip creating initializer job
if !isCloudTestRun(k6) && k6.IsInitializerDisabled() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to take into account the --out cloud argument only, not all Cloud tests. So if that argument is present, initializer.disabled is discarded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Option in TestRun CRD to switch off initializer

2 participants