Repository: grafana/k6-operator (go)

Issue:
Image names should be more specific
### Feature Description

We're building all images for k6-operator under the prefix `ghcr.io/grafana/operator`. This might be confusing since Grafana has other operators (which are not hosted on `ghcr.io`, AFAIK, so there is no conflict yet). Still, it's best if we change the prefix for images to something like `ghcr.io/grafana/k6-operator`.

Once implemented, this change somewhat forces people to adjust their setups around the k6-operator and in their Dockerfiles if they're building from our runner image. So if someone has opinions to share, they're welcome to.

### Suggested Solution (optional)

_No response_

### Already existing or connected issues / PRs (optional)

_No response_

Pull request description:
Update image names to k6-operator over generic operator
Fixes #221

Patch:
diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml
index 8157ad2..1d4ff99 100644
--- a/.github/workflows/push.yaml
+++ b/.github/workflows/push.yaml
@@ -46,18 +46,18 @@ jobs:
           file: Dockerfile.controller
           build-args: |
             GO_BUILDER_IMG=golang:1.18
-          tags: ghcr.io/grafana/operator:latest,ghcr.io/grafana/operator:controller-${{env.IMAGETAG}}
+          tags: ghcr.io/grafana/k6-operator:latest,ghcr.io/grafana/k6-operator:controller-${{env.IMAGETAG}}
       - name: "Build:dockerimage"
         uses: docker/build-push-action@v2
         with:
           context: .
           push: true
           file: Dockerfile.runner
-          tags: ghcr.io/grafana/operator:latest-runner,ghcr.io/grafana/operator:runner-${{env.IMAGETAG}}
+          tags: ghcr.io/grafana/k6-operator:latest-runner,ghcr.io/grafana/k6-operator:runner-${{env.IMAGETAG}}
       - name: "Build:dockerimage"
         uses: docker/build-push-action@v2
         with:
           context: .
           push: true
           file: Dockerfile.starter
-          tags: ghcr.io/grafana/operator:latest-starter,ghcr.io/grafana/operator:starter-${{env.IMAGETAG}}
+          tags: ghcr.io/grafana/k6-operator:latest-starter,ghcr.io/grafana/k6-operator:starter-${{env.IMAGETAG}}
diff --git a/Makefile b/Makefile
index 37517d7..e43b3b0 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL)
 # Image to use for building Go
 GO_BUILDER_IMG ?= "golang:1.18"
 # Image URL to use all building/pushing image targets
-IMG ?= ghcr.io/grafana/operator:latest
+IMG ?= ghcr.io/grafana/k6-operator:latest
 # Default dockerfile to build
 DOCKERFILE ?= "Dockerfile.controller"
 # Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
diff --git a/README.md b/README.md
index 064fcd4..a971d14 100644
--- a/README.md
+++ b/README.md
@@ -8,11 +8,17 @@ Read also the [complete tutorial](https://k6.io/blog/running-distributed-tests-o
 
 ## Setup
 
+### Prerequisites
+In order to install the operator, the following additional tooling must be installed:
+- [go](https://go.dev/doc/install)
+- [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl)
+- [kustomize](https://kubectl.docs.kubernetes.io/installation/kustomize/)
+
 ### Deploying the operator
 Install the operator by running the command below:
 
 ```bash
-$ make deploy
+make deploy
 ```
 
 ### Installing the CRD
@@ -21,7 +27,7 @@ The k6 operator includes one custom resource called `K6`. This will be automatic
 deployment, but in case you want to do it yourself, you may run the command below:
 
 ```bash
-$ make install
+make install
 ```
 
 ## Usage
@@ -34,7 +40,7 @@ The operator utilises `ConfigMap`s and `LocalFile` to serve test scripts to the
 
 #### ConfigMap
 ```bash
-$ kubectl create configmap my-test --from-file /path/to/my/test.js
+kubectl create configmap my-test --from-file /path/to/my/test.js
 ```
 
 ***Note: there is a character limit of 1048576 bytes to a single configmap. If you need to have a larger test file, you'll need to use a volumeClaim or a LocalFile instead***
@@ -155,7 +161,7 @@ spec:
 The test configuration is applied using
 
 ```bash
-$ kubectl apply -f /path/to/your/k6-resource.yml
+kubectl apply -f /path/to/your/k6-resource.yml
 ```
 
 #### Parallelism
@@ -255,7 +261,7 @@ export let options = {
 ### Cleaning up between test runs
 After completing a test run, you need to clean up the test jobs created. This is done by running the following command:
 ```bash
-$ kubectl delete -f /path/to/your/k6-resource.yml
+kubectl delete -f /path/to/your/k6-resource.yml
 ```
 
 ### Multi-file tests
@@ -295,25 +301,32 @@ spec:
 In other words, `file` option must be the correct entrypoint for `k6 run`.
 
 ### Using extensions
-By default, the operator will use `grafana/k6:latest` as the container image for the test jobs. If you want to use
-extensions built with [xk6](https://github.com/grafana/xk6) you'll need to create your own image and override the `image`
-property on the `K6` kubernetes resource. For example, the following Dockerfile can be used to create a container
-image using `https://github.com/grafana/xk6-output-prometheus-remote` as an extension:
+By default, the operator will use `grafana/k6:latest` as the container image for the test jobs. 
+If you want to use [extensions](https://k6.io/docs/extensions/get-started/explore/) built with [xk6](https://github.com/grafana/xk6) you'll need to create your own image and override the `image` property on the `K6` kubernetes resource. 
 
+For example, create a `Dockerfile` with the following content:
 
 ```Dockerfile
 # Build the k6 binary with the extension
-FROM golang:1.18.1 as builder
+FROM golang:1.20 as builder
 
 RUN go install go.k6.io/xk6/cmd/xk6@latest
-RUN xk6 build --output /k6 --with github.com/grafana/xk6-output-prometheus-remote@latest
+# For our example, we'll add support for output of test metrics to InfluxDB v2.
+# Feel free to add other extensions using the '--with ...'.
+RUN xk6 build \
+    --with github.com/grafana/xk6-output-influxdb@latest \
+    --output /k6
 
 # Use the operator's base image and override the k6 binary
 FROM grafana/k6:latest
 COPY --from=builder /k6 /usr/bin/k6
 ```
-
-If we build and tag this image as `k6-prometheus:local`, then we can use it as follows:
+Build the image based on this `Dockerfile` by executing: 
+```bash
+docker build -t k6-extended:local .
+```
+Once the build is completed, push the resulting `k6-extended:local` image to an image repository accessible to your Kubernetes cluster.
+We can now use it as follows:
 
 ```yaml
 # k6-resource-with-extensions.yml
@@ -328,31 +341,14 @@ spec:
     configMap:
       name: crocodile-stress-test
       file: test.js
-  arguments: -o xk6-prometheus-rw
   runner:
-    image: k6-prometheus:local
+    image: k6-extended:local
     env:
-      - name: K6_PROMETHEUS_RW_SERVER_URL
-        value: http://prometheus.somewhere:9090/api/v1/write
+      - name: K6_OUT
+        value: xk6-influxdb=http://influxdb.somewhere:8086/demo
 ```
 
-Note that we are replacing the test job image (`k6-prometheus:latest`), passing required arguments to `k6`
-(`-o xk6-prometheus-rw`), and also setting the environment variable to the runner (`K6_PROMETHEUS_RW_SERVER_URL`).
-
-<!-- If using the Prometheus Operator, you'll also need to create a pod monitor:
-
-```yaml
-apiVersion: monitoring.coreos.com/v1
-kind: PodMonitor
-metadata:
-  name: k6-monitor
-spec:
-  selector:
-    matchLabels:
-      app: k6
-  podMetricsEndpoints:
-  - port: metrics
-``` -->
+Note that we are overriding the default image with `k6-extended:latest`, providing the test runner with environment variables used by our included extensions.
 
 ### Scheduling Tests
 
@@ -451,7 +447,6 @@ spec:
                 name: <test-name>-config
 ```
 
-
 ## Uninstallation
 Running the command below will delete all resources created by the operator.
 ```bash
diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml
index a21f98a..43f5e7e 100644
--- a/config/manager/kustomization.yaml
+++ b/config/manager/kustomization.yaml
@@ -1,9 +1,8 @@
----
 resources:
 - manager.yaml
 images:
 - name: controller
-  newName: ghcr.io/grafana/operator
+  newName: ghcr.io/grafana/k6-operator
   newTag: latest
 apiVersion: kustomize.config.k8s.io/v1beta1
 kind: Kustomization
diff --git a/config/manifests/bases/k6-operator.clusterserviceversion.yaml b/config/manifests/bases/k6-operator.clusterserviceversion.yaml
index 2280cc3..b7584b1 100644
--- a/config/manifests/bases/k6-operator.clusterserviceversion.yaml
+++ b/config/manifests/bases/k6-operator.clusterserviceversion.yaml
@@ -5,7 +5,7 @@ metadata:
   annotations:
     alm-examples: "[]"
     capabilities: Basic Install
-    containerImage: ghcr.io/grafana/operator
+    containerImage: ghcr.io/grafana/k6-operator
     repository: https://github.com/grafana/k6-operator
     support: k6
   name: k6-operator.v0.0.0
diff --git a/controllers/k6_initialize.go b/controllers/k6_initialize.go
index a1c6a2c..7de9358 100644
--- a/controllers/k6_initialize.go
+++ b/controllers/k6_initialize.go
@@ -132,6 +132,13 @@ func SetupCloudTest(ctx context.Context, log logr.Logger, k6 *v1alpha1.K6, r *K6
 			return ctrl.Result{RequeueAfter: time.Second * 2}, nil
 		}
 
+		if len(inspectOutput.External.Loadimpact.Name) < 1 {
+			// script has already been parsed for initializer job definition,
+			// so this is safe
+			script, _ := k6.Spec.Script.Parse()
+			inspectOutput.External.Loadimpact.Name = script.Filename
+		}
+
 		if testRunData, err := cloud.CreateTestRun(inspectOutput, k6.Spec.Parallelism, host, token, log); err != nil {
 			log.Error(err, "Failed to create a new cloud test run.")
 			return res, nil
diff --git a/pkg/cloud/cloud_output.go b/pkg/cloud/cloud_output.go
index b9cbe0b..29f8a36 100644
--- a/pkg/cloud/cloud_output.go
+++ b/pkg/cloud/cloud_output.go
@@ -9,7 +9,7 @@ import (
 	"github.com/sirupsen/logrus"
 	"go.k6.io/k6/cloudapi"
 	"go.k6.io/k6/lib/consts"
-	"gopkg.in/guregu/null.v3"
+	null "gopkg.in/guregu/null.v3"
 )
 
 var client *cloudapi.Client
@@ -25,10 +25,6 @@ type TestRun struct {
 }
 
 func CreateTestRun(opts InspectOutput, instances int32, host, token string, log logr.Logger) (*cloudapi.CreateTestRunResponse, error) {
-	if len(opts.External.Loadimpact.Name) < 1 {
-		opts.External.Loadimpact.Name = "k6-operator-test"
-	}
-
 	cloudConfig := cloudapi.NewConfig()
 
 	if opts.External.Loadimpact.ProjectID > 0 {
@@ -57,7 +53,7 @@ func CreateTestRun(opts InspectOutput, instances int32, host, token string, log
 		client = cloudapi.NewClient(logger, token, host, consts.Version, time.Duration(time.Minute))
 	}
 
-	return createTestRun(client, host, &TestRun{
+	tr := TestRun{
 		Name:              opts.External.Loadimpact.Name,
 		ProjectID:         cloudConfig.ProjectID.Int64,
 		VUsMax:            int64(opts.MaxVUs),
@@ -65,7 +61,8 @@ func CreateTestRun(opts InspectOutput, instances int32, host, token string, log
 		Duration:          int64(opts.TotalDuration.TimeDuration().Seconds()),
 		ProcessThresholds: true,
 		Instances:         instances,
-	})
+	}
+	return createTestRun(client, host, &tr)
 }
 
 // We cannot use cloudapi.TestRun struct and cloudapi.Client.CreateTestRun call because they're not aware of
diff --git a/pkg/resources/jobs/initializer.go b/pkg/resources/jobs/initializer.go
index 581256e..42f4829 100644
--- a/pkg/resources/jobs/initializer.go
+++ b/pkg/resources/jobs/initializer.go
@@ -18,7 +18,7 @@ func NewInitializerJob(k6 *v1alpha1.K6, argLine string) (*batchv1.Job, error) {
 	}
 
 	var (
-		image                        = "ghcr.io/grafana/operator:latest-runner"
+		image                        = "ghcr.io/grafana/k6-operator:latest-runner"
 		annotations                  = make(map[string]string)
 		labels                       = newLabels(k6.Name)
 		serviceAccountName           = "default"
diff --git a/pkg/resources/jobs/runner.go b/pkg/resources/jobs/runner.go
index ea2df49..26e13a7 100644
--- a/pkg/resources/jobs/runner.go
+++ b/pkg/resources/jobs/runner.go
@@ -80,7 +80,7 @@ func NewRunnerJob(k6 *v1alpha1.K6, index int, token string) (*batchv1.Job, error
 		zero32 int32 = 0
 	)
 
-	image := "ghcr.io/grafana/operator:latest-runner"
+	image := "ghcr.io/grafana/k6-operator:latest-runner"
 	if k6.Spec.Runner.Image != "" {
 		image = k6.Spec.Runner.Image
 	}
diff --git a/pkg/resources/jobs/starter.go b/pkg/resources/jobs/starter.go
index 7541f1d..af9e963 100644
--- a/pkg/resources/jobs/starter.go
+++ b/pkg/resources/jobs/starter.go
@@ -19,7 +19,7 @@ func NewStarterJob(k6 *v1alpha1.K6, hostname []string) *batchv1.Job {
 		starterAnnotations = k6.Spec.Starter.Metadata.Annotations
 	}
 
-	starterImage := "ghcr.io/grafana/operator:latest-starter"
+	starterImage := "ghcr.io/grafana/k6-operator:latest-starter"
 	if k6.Spec.Starter.Image != "" {
 		starterImage = k6.Spec.Starter.Image
 	}


Test patch:
diff --git a/.github/workflows/e2e-test.yaml b/.github/workflows/e2e-test.yaml
index a108b5f..3fadf6c 100644
--- a/.github/workflows/e2e-test.yaml
+++ b/.github/workflows/e2e-test.yaml
@@ -25,7 +25,7 @@ jobs:
           build-args: |
             GO_BUILDER_IMG=golang:1.18
           tags: |
-            ghcr.io/grafana/operator:${{ github.sha }}
+            ghcr.io/grafana/k6-operator:${{ github.sha }}
   kind:
     runs-on: ubuntu-latest
     steps:
@@ -44,7 +44,7 @@ jobs:
         run: |
           kubectl cluster-info
           kubectl apply -f rendered.yaml
-          kubectl -n k6-operator-system set image deployment/k6-operator-controller-manager manager=ghcr.io/grafana/operator:${{ github.sha }}
+          kubectl -n k6-operator-system set image deployment/k6-operator-controller-manager manager=ghcr.io/grafana/k6-operator:${{ github.sha }}
           kubectl create configmap crocodile-stress-test --from-file e2e/test.js
           kubectl apply -f e2e/test.yaml
           sleep 30
diff --git a/pkg/resources/jobs/initializer_test.go b/pkg/resources/jobs/initializer_test.go
index f475a35..923d8c9 100644
--- a/pkg/resources/jobs/initializer_test.go
+++ b/pkg/resources/jobs/initializer_test.go
@@ -56,7 +56,7 @@ func TestNewInitializerJob(t *testing.T) {
 					SecurityContext:              &corev1.PodSecurityContext{},
 					Containers: []corev1.Container{
 						{
-							Image:           "ghcr.io/grafana/operator:latest-runner",
+							Image:           "ghcr.io/grafana/k6-operator:latest-runner",
 							ImagePullPolicy: "",
 							Name:            "k6",
 							Command: []string{
diff --git a/pkg/resources/jobs/runner_test.go b/pkg/resources/jobs/runner_test.go
index f755b25..c89dcb3 100644
--- a/pkg/resources/jobs/runner_test.go
+++ b/pkg/resources/jobs/runner_test.go
@@ -320,7 +320,7 @@ func TestNewRunnerJob(t *testing.T) {
 					ServiceAccountName:           "default",
 					AutomountServiceAccountToken: &automountServiceAccountToken,
 					Containers: []corev1.Container{{
-						Image:           "ghcr.io/grafana/operator:latest-runner",
+						Image:           "ghcr.io/grafana/k6-operator:latest-runner",
 						ImagePullPolicy: corev1.PullNever,
 						Name:            "k6",
 						Command:         []string{"k6", "run", "--quiet", "/test/test.js", "--address=0.0.0.0:6565", "--paused", "--tag", "instance_id=1", "--tag", "job_name=test-1"},
@@ -451,7 +451,7 @@ func TestNewRunnerJobNoisy(t *testing.T) {
 					AutomountServiceAccountToken: &automountServiceAccountToken,
 					SecurityContext:              &corev1.PodSecurityContext{},
 					Containers: []corev1.Container{{
-						Image:           "ghcr.io/grafana/operator:latest-runner",
+						Image:           "ghcr.io/grafana/k6-operator:latest-runner",
 						ImagePullPolicy: "",
 						Name:            "k6",
 						Command:         []string{"k6", "run", "/test/test.js", "--address=0.0.0.0:6565", "--paused", "--tag", "instance_id=1", "--tag", "job_name=test-1"},
@@ -564,7 +564,7 @@ func TestNewRunnerJobUnpaused(t *testing.T) {
 					AutomountServiceAccountToken: &automountServiceAccountToken,
 					SecurityContext:              &corev1.PodSecurityContext{},
 					Containers: []corev1.Container{{
-						Image:           "ghcr.io/grafana/operator:latest-runner",
+						Image:           "ghcr.io/grafana/k6-operator:latest-runner",
 						ImagePullPolicy: "",
 						Name:            "k6",
 						Command:         []string{"k6", "run", "--quiet", "/test/test.js", "--address=0.0.0.0:6565", "--tag", "instance_id=1", "--tag", "job_name=test-1"},
@@ -677,7 +677,7 @@ func TestNewRunnerJobArguments(t *testing.T) {
 					AutomountServiceAccountToken: &automountServiceAccountToken,
 					SecurityContext:              &corev1.PodSecurityContext{},
 					Containers: []corev1.Container{{
-						Image:           "ghcr.io/grafana/operator:latest-runner",
+						Image:           "ghcr.io/grafana/k6-operator:latest-runner",
 						ImagePullPolicy: "",
 						Name:            "k6",
 						Command:         []string{"k6", "run", "--quiet", "--cool-thing", "/test/test.js", "--address=0.0.0.0:6565", "--paused", "--tag", "instance_id=1", "--tag", "job_name=test-1"},
@@ -791,7 +791,7 @@ func TestNewRunnerJobServiceAccount(t *testing.T) {
 					AutomountServiceAccountToken: &automountServiceAccountToken,
 					SecurityContext:              &corev1.PodSecurityContext{},
 					Containers: []corev1.Container{{
-						Image:           "ghcr.io/grafana/operator:latest-runner",
+						Image:           "ghcr.io/grafana/k6-operator:latest-runner",
 						ImagePullPolicy: "",
 						Name:            "k6",
 						Command:         []string{"k6", "run", "--quiet", "/test/test.js", "--address=0.0.0.0:6565", "--paused", "--tag", "instance_id=1", "--tag", "job_name=test-1"},
@@ -906,7 +906,7 @@ func TestNewRunnerJobIstio(t *testing.T) {
 					AutomountServiceAccountToken: &automountServiceAccountToken,
 					SecurityContext:              &corev1.PodSecurityContext{},
 					Containers: []corev1.Container{{
-						Image:           "ghcr.io/grafana/operator:latest-runner",
+						Image:           "ghcr.io/grafana/k6-operator:latest-runner",
 						ImagePullPolicy: "",
 						Name:            "k6",
 						Command:         []string{"scuttle", "k6", "run", "--quiet", "/test/test.js", "--address=0.0.0.0:6565", "--paused", "--tag", "instance_id=1", "--tag", "job_name=test-1"},
@@ -1033,7 +1033,7 @@ func TestNewRunnerJobCloud(t *testing.T) {
 					SecurityContext:              &corev1.PodSecurityContext{},
 					AutomountServiceAccountToken: &automountServiceAccountToken,
 					Containers: []corev1.Container{{
-						Image:           "ghcr.io/grafana/operator:latest-runner",
+						Image:           "ghcr.io/grafana/k6-operator:latest-runner",
 						ImagePullPolicy: "",
 						Name:            "k6",
 						Command:         []string{"k6", "run", "--quiet", "--out", "cloud", "/test/test.js", "--address=0.0.0.0:6565", "--paused", "--tag", "instance_id=1", "--tag", "job_name=test-1"},
@@ -1157,7 +1157,7 @@ func TestNewRunnerJobLocalFile(t *testing.T) {
 					AutomountServiceAccountToken: &automountServiceAccountToken,
 					SecurityContext:              &corev1.PodSecurityContext{},
 					Containers: []corev1.Container{{
-						Image:           "ghcr.io/grafana/operator:latest-runner",
+						Image:           "ghcr.io/grafana/k6-operator:latest-runner",
 						ImagePullPolicy: "",
 						Name:            "k6",
 						Command:         []string{"sh", "-c", "if [ ! -f /test/test.js ]; then echo \"LocalFile not found exiting...\"; exit 1; fi;\nk6 run --quiet /test/test.js --address=0.0.0.0:6565 --paused --tag instance_id=1 --tag job_name=test-1"},
@@ -1287,7 +1287,7 @@ func TestNewRunnerJobWithInitContainer(t *testing.T) {
 						},
 					},
 					Containers: []corev1.Container{{
-						Image:           "ghcr.io/grafana/operator:latest-runner",
+						Image:           "ghcr.io/grafana/k6-operator:latest-runner",
 						ImagePullPolicy: corev1.PullNever,
 						Name:            "k6",
 						Command:         []string{"k6", "run", "--quiet", "/test/test.js", "--address=0.0.0.0:6565", "--paused", "--tag", "instance_id=1", "--tag", "job_name=test-1"},