Repository: grafana/k6-operator
Pull request #185 (2023-02-01 15:56:19)

Allow liveness and readiness probes for runner pods
As discussed in https://github.com/grafana/k6-operator/pull/169#discussion_r1073534803, I open in a separate PR this new feature.


Patch:
diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml
index c1ab27f..480df66 100644
--- a/.github/workflows/push.yaml
+++ b/.github/workflows/push.yaml
@@ -2,6 +2,11 @@
 name: "push"
 
 on:
+  workflow_dispatch:
+    inputs:
+      image_tag:
+        description: 'Image tag name.'
+        required: true
   release:
     types:
       - published
@@ -13,8 +18,20 @@ jobs:
     steps:
       - name: "Build:checkout"
         uses: actions/checkout@v2
+      - name: "Set image tag name"
+        run: |
+          if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
+            echo "IMAGETAG=${{ github.event.inputs.image_tag }}" >> $GITHUB_ENV
+          else
+            echo "IMAGETAG=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
+          fi
+      - name: "Check image tag name"
+        run: |
+          echo "IMAGETAG=${{env.IMAGETAG}}"
       - name: "Build:buildx"
         uses: docker/setup-buildx-action@v1
+        with:
+          version: v0.9.1 # Buildx version
       - name: "Build:login"
         uses: docker/login-action@v1
         with:
@@ -29,18 +46,18 @@ jobs:
           file: Dockerfile.controller
           build-args: |
             GO_BUILDER_IMG=golang:1.17
-          tags: ghcr.io/grafana/operator:latest,ghcr.io/grafana/operator:controller-${{ github.event.release.tag_name }}
+          tags: ghcr.io/grafana/operator:latest,ghcr.io/grafana/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-${{ github.event.release.tag_name }}
+          tags: ghcr.io/grafana/operator:latest-runner,ghcr.io/grafana/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-${{ github.event.release.tag_name }}
+          tags: ghcr.io/grafana/operator:latest-starter,ghcr.io/grafana/operator:starter-${{env.IMAGETAG}}
diff --git a/api/v1alpha1/k6_types.go b/api/v1alpha1/k6_types.go
index ddeb3bb..97eb3ed 100644
--- a/api/v1alpha1/k6_types.go
+++ b/api/v1alpha1/k6_types.go
@@ -45,7 +45,7 @@ type K6Scuttle struct {
 	EnvoyAdminApi           string `json:"envoyAdminApi,omitempty"`
 	NeverKillIstio          bool   `json:"neverKillIstio,omitempty"`
 	NeverKillIstioOnFailure bool   `json:"neverKillIstioOnFailure,omitempty"`
-	ScuttleLogging          bool   `json:"scuttleLogging,omitempty"`
+	DisableLogging          bool   `json:"disableLogging,omitempty"`
 	StartWithoutEnvoy       bool   `json:"startWithoutEnvoy,omitempty"`
 	WaitForEnvoyTimeout     string `json:"waitForEnvoyTimeout,omitempty"`
 	IstioQuitApi            string `json:"istioQuitApi,omitempty"`
diff --git a/config/crd/bases/k6.io_k6s.yaml b/config/crd/bases/k6.io_k6s.yaml
index 8069f42..4a3303e 100644
--- a/config/crd/bases/k6.io_k6s.yaml
+++ b/config/crd/bases/k6.io_k6s.yaml
@@ -1114,6 +1114,8 @@ spec:
                 type: object
               scuttle:
                 properties:
+                  disableLogging:
+                    type: boolean
                   enabled:
                     type: string
                   envoyAdminApi:
@@ -1128,8 +1130,6 @@ spec:
                     type: boolean
                   quitWithoutEnvoyTimeout:
                     type: string
-                  scuttleLogging:
-                    type: boolean
                   startWithoutEnvoy:
                     type: boolean
                   waitForEnvoyTimeout:
diff --git a/pkg/cloud/cloud.go b/pkg/cloud/cloud.go
index 6e8965c..954e453 100644
--- a/pkg/cloud/cloud.go
+++ b/pkg/cloud/cloud.go
@@ -11,6 +11,7 @@ import (
 	"go.k6.io/k6/lib"
 	"go.k6.io/k6/lib/consts"
 	"go.k6.io/k6/lib/types"
+	"go.k6.io/k6/stats"
 	"gopkg.in/guregu/null.v3"
 )
 
@@ -23,9 +24,9 @@ type InspectOutput struct {
 			ProjectID int64  `json:"projectID"`
 		} `json:"loadimpact"`
 	} `json:"ext"`
-	TotalDuration types.NullDuration  `json:"totalDuration"`
-	MaxVUs        uint64              `json:"maxVUs"`
-	Thresholds    map[string][]string `json:"thresholds,omitempty"`
+	TotalDuration types.NullDuration           `json:"totalDuration"`
+	MaxVUs        uint64                       `json:"maxVUs"`
+	Thresholds    map[string]*stats.Thresholds `json:"thresholds,omitempty"`
 }
 
 type TestRun struct {
@@ -56,8 +57,11 @@ func CreateTestRun(opts InspectOutput, instances int32, host, token string, log
 		Level:     logrus.InfoLevel,
 	}
 
-	if opts.Thresholds == nil {
-		opts.Thresholds = make(map[string][]string)
+	thresholds := make(map[string][]string, len(opts.Thresholds))
+	for name, t := range opts.Thresholds {
+		for _, threshold := range t.Thresholds {
+			thresholds[name] = append(thresholds[name], threshold.Source)
+		}
 	}
 
 	if len(host) == 0 {
@@ -69,7 +73,7 @@ func CreateTestRun(opts InspectOutput, instances int32, host, token string, log
 		Name:              opts.External.Loadimpact.Name,
 		ProjectID:         cloudConfig.ProjectID.Int64,
 		VUsMax:            int64(opts.MaxVUs),
-		Thresholds:        opts.Thresholds,
+		Thresholds:        thresholds,
 		Duration:          int64(opts.TotalDuration.TimeDuration().Seconds()),
 		ProcessThresholds: true,
 		Instances:         instances,
diff --git a/pkg/resources/jobs/helpers.go b/pkg/resources/jobs/helpers.go
index 3a8110a..b0d09b5 100644
--- a/pkg/resources/jobs/helpers.go
+++ b/pkg/resources/jobs/helpers.go
@@ -84,10 +84,10 @@ func newIstioEnvVar(istio v1alpha1.K6Scuttle, istioEnabled bool) []corev1.EnvVar
 			})
 		}
 
-		if istio.ScuttleLogging {
+		if istio.DisableLogging {
 			env = append(env, corev1.EnvVar{
 				Name:  "SCUTTLE_LOGGING",
-				Value: strconv.FormatBool(istio.ScuttleLogging),
+				Value: strconv.FormatBool(false),
 			})
 		}
 
diff --git a/pkg/resources/jobs/initializer.go b/pkg/resources/jobs/initializer.go
index 59e34d9..3837f8e 100644
--- a/pkg/resources/jobs/initializer.go
+++ b/pkg/resources/jobs/initializer.go
@@ -54,7 +54,7 @@ func NewInitializerJob(k6 *v1alpha1.K6, argLine string) (*batchv1.Job, error) {
 	var (
 		// k6 allows to run archive command on archives too so type of file here doesn't matter
 		scriptName  = script.FullName()
-		archiveName = fmt.Sprintf("./%s.archived.tar", script.Filename)
+		archiveName = fmt.Sprintf("/tmp/%s.archived.tar", script.Filename)
 	)
 	command, istioEnabled := newIstioCommand(k6.Spec.Scuttle.Enabled, []string{"sh", "-c"})
 	command = append(command, fmt.Sprintf(
diff --git a/pkg/resources/jobs/runner.go b/pkg/resources/jobs/runner.go
index 48d0c90..8bcb815 100644
--- a/pkg/resources/jobs/runner.go
+++ b/pkg/resources/jobs/runner.go
@@ -2,6 +2,7 @@ package jobs
 
 import (
 	"fmt"
+	"k8s.io/apimachinery/pkg/util/intstr"
 	"strconv"
 
 	"strings"
@@ -160,6 +161,24 @@ func NewRunnerJob(k6 *v1alpha1.K6, index int, testRunId, token string) (*batchv1
 						VolumeMounts:    script.VolumeMount(),
 						Ports:           ports,
 						EnvFrom:         k6.Spec.Runner.EnvFrom,
+						LivenessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
+						ReadinessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
 					}},
 					TerminationGracePeriodSeconds: &zero,
 					Volumes:                       script.Volume(),


Test patch:
diff --git a/pkg/resources/jobs/helpers_test.go b/pkg/resources/jobs/helpers_test.go
index 70def50..eee2eb2 100644
--- a/pkg/resources/jobs/helpers_test.go
+++ b/pkg/resources/jobs/helpers_test.go
@@ -110,7 +110,7 @@ func TestNewIstioEnvVarTrueValues(t *testing.T) {
 		},
 		{
 			Name:  "SCUTTLE_LOGGING",
-			Value: "true",
+			Value: "false",
 		},
 	}
 
@@ -118,7 +118,7 @@ func TestNewIstioEnvVarTrueValues(t *testing.T) {
 		EnvoyAdminApi:       "",
 		IstioQuitApi:        "",
 		WaitForEnvoyTimeout: "",
-		ScuttleLogging:      true,
+		DisableLogging:      true,
 	}, true)
 
 	if !reflect.DeepEqual(envVars, expectedOutcome) {
@@ -146,7 +146,7 @@ func TestNewIstioEnvVarFalseValues(t *testing.T) {
 		EnvoyAdminApi:       "",
 		IstioQuitApi:        "",
 		WaitForEnvoyTimeout: "",
-		ScuttleLogging:      false,
+		DisableLogging:      false,
 	}, true)
 
 	if !reflect.DeepEqual(envVars, expectedOutcome) {
diff --git a/pkg/resources/jobs/initializer_test.go b/pkg/resources/jobs/initializer_test.go
index 53248e3..861025c 100644
--- a/pkg/resources/jobs/initializer_test.go
+++ b/pkg/resources/jobs/initializer_test.go
@@ -60,7 +60,7 @@ func TestNewInitializerJob(t *testing.T) {
 							Name:            "k6",
 							Command: []string{
 								"sh", "-c",
-								"k6 archive /test/test.js -O ./test.js.archived.tar --out cloud 2> /tmp/k6logs && k6 inspect --execution-requirements ./test.js.archived.tar 2> /tmp/k6logs ; ! cat /tmp/k6logs | grep 'level=error'",
+								"k6 archive /test/test.js -O /tmp/test.js.archived.tar --out cloud 2> /tmp/k6logs && k6 inspect --execution-requirements /tmp/test.js.archived.tar 2> /tmp/k6logs ; ! cat /tmp/k6logs | grep 'level=error'",
 							},
 							Env:          []corev1.EnvVar{},
 							Resources:    corev1.ResourceRequirements{},
diff --git a/pkg/resources/jobs/runner_test.go b/pkg/resources/jobs/runner_test.go
index 2c6498f..aeaaada 100644
--- a/pkg/resources/jobs/runner_test.go
+++ b/pkg/resources/jobs/runner_test.go
@@ -2,6 +2,7 @@ package jobs
 
 import (
 	"errors"
+	"k8s.io/apimachinery/pkg/util/intstr"
 	"reflect"
 	"testing"
 
@@ -312,6 +313,24 @@ func TestNewRunnerJob(t *testing.T) {
 								},
 							},
 						},
+						LivenessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
+						ReadinessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
 					}},
 					TerminationGracePeriodSeconds: &zero,
 					Volumes:                       script.Volume(),
@@ -416,6 +435,24 @@ func TestNewRunnerJobNoisy(t *testing.T) {
 						Resources:       corev1.ResourceRequirements{},
 						VolumeMounts:    script.VolumeMount(),
 						Ports:           []corev1.ContainerPort{{ContainerPort: 6565}},
+						LivenessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
+						ReadinessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
 					}},
 					TerminationGracePeriodSeconds: &zero,
 					Volumes:                       script.Volume(),
@@ -511,6 +548,24 @@ func TestNewRunnerJobUnpaused(t *testing.T) {
 						Resources:       corev1.ResourceRequirements{},
 						VolumeMounts:    script.VolumeMount(),
 						Ports:           []corev1.ContainerPort{{ContainerPort: 6565}},
+						LivenessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
+						ReadinessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
 					}},
 					TerminationGracePeriodSeconds: &zero,
 					Volumes:                       script.Volume(),
@@ -606,6 +661,24 @@ func TestNewRunnerJobArguments(t *testing.T) {
 						Resources:       corev1.ResourceRequirements{},
 						VolumeMounts:    script.VolumeMount(),
 						Ports:           []corev1.ContainerPort{{ContainerPort: 6565}},
+						LivenessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
+						ReadinessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
 					}},
 					TerminationGracePeriodSeconds: &zero,
 					Volumes:                       script.Volume(),
@@ -702,6 +775,24 @@ func TestNewRunnerJobServiceAccount(t *testing.T) {
 						Resources:       corev1.ResourceRequirements{},
 						VolumeMounts:    script.VolumeMount(),
 						Ports:           []corev1.ContainerPort{{ContainerPort: 6565}},
+						LivenessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
+						ReadinessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
 					}},
 					TerminationGracePeriodSeconds: &zero,
 					Volumes:                       script.Volume(),
@@ -812,6 +903,24 @@ func TestNewRunnerJobIstio(t *testing.T) {
 						Resources:    corev1.ResourceRequirements{},
 						VolumeMounts: script.VolumeMount(),
 						Ports:        []corev1.ContainerPort{{ContainerPort: 6565}},
+						LivenessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
+						ReadinessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
 					}},
 					TerminationGracePeriodSeconds: &zero,
 					Volumes:                       script.Volume(),
@@ -917,6 +1026,24 @@ func TestNewRunnerJobCloud(t *testing.T) {
 						Resources:    corev1.ResourceRequirements{},
 						VolumeMounts: script.VolumeMount(),
 						Ports:        []corev1.ContainerPort{{ContainerPort: 6565}},
+						LivenessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
+						ReadinessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
 					}},
 					TerminationGracePeriodSeconds: &zero,
 					Volumes:                       script.Volume(),
@@ -1008,6 +1135,24 @@ func TestNewRunnerJobLocalFile(t *testing.T) {
 						Resources:       corev1.ResourceRequirements{},
 						VolumeMounts:    script.VolumeMount(),
 						Ports:           []corev1.ContainerPort{{ContainerPort: 6565}},
+						LivenessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
+						ReadinessProbe: &corev1.Probe{
+							Handler: corev1.Handler{
+								HTTPGet: &corev1.HTTPGetAction{
+									Path:   "/v1/status",
+									Port:   intstr.IntOrString{IntVal: 6565},
+									Scheme: "HTTP",
+								},
+							},
+						},
 					}},
 					TerminationGracePeriodSeconds: &zero,
 					Volumes:                       script.Volume(),