Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport to v2.2.0 release branch] Soak tests on Kubernetes (#1821) #1869

Open
wants to merge 1 commit into
base: release/v2.2.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions tests/soak/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM debian:bookworm
ARG LK_VERSION
ARG CKPY_VERSION
RUN test -n "${LK_VERSION}" || (echo "LK_VERSION env variable required" && exit 1)
RUN test -n "${CKPY_VERSION}" || (echo "CKPY_VERSION env variable required" && exit 1)
ENV DEBIAN_FRONTEND=noninteractive
RUN apt update && apt install -y sudo
RUN mkdir -p /soaktests
COPY bootstrap.sh /soaktests
WORKDIR /soaktests
RUN /soaktests/bootstrap.sh ${CKPY_VERSION} ${LK_VERSION}
ENTRYPOINT [ "/soaktests/confluent-kafka-python/tests/soak/run.sh" ]
30 changes: 9 additions & 21 deletions tests/soak/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,14 @@ of time, typically 2+ weeks, to vet out any resource leaks, etc.
The soak testing client is made up of a producer, producing messages to
the configured topic, and a consumer, consuming the same messages back.

DataDog reporting supported by setting datadog.api_key a and datadog.app_key
in the soak client configuration file.
OpenTelemetry reporting supported through OTLP.

# Installation

There are some convenience script to get you started.

On the host (ec2) where you aim to run the soaktest, do:

$ git clone https://github.com/confluentinc/librdkafka
$ git clone https://github.com/confluentinc/confluent-kafka-python

# Build librdkafka and python
$ ~/confluent-kafka-python/tests/soak/build.sh <librdkafka-version> <cfl-python-version>

# Set up config:
$ cp ~/confluent-kafka-python/tests/soak/ccloud.config.example ~/confluent-kafka-python/ccloud.config

# Start a screen session
$ screen bash

# Within the screen session, run the soak client
(screen)$ ~/run.sh
(screen)$ Ctrl-A d # to detach
TESTID=normal \
LK_VERSION=v2.2.0 \
CKPY_VERSION=v2.2.0 \
CC_BOOSTRAP_SERVERS=_ \
CC_USERNAME=_ \
CC_PASSWORD=_ \
DOCKER_REPOSITORY=_ ./install.sh
17 changes: 7 additions & 10 deletions tests/soak/ubuntu-bootstrap.sh → tests/soak/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ fi

python_branch=$1
librdkafka_branch=$2
venv=$PWD/venv

sudo apt update
sudo apt install -y make gcc g++ zlib1g-dev libssl-dev libzstd-dev screen \
python3.6-dev python3-pip python3-virtualenv

pushd $HOME
sudo apt install -y git curl make gcc g++ zlib1g-dev libssl-dev libzstd-dev \
python3-dev python3-pip python3-venv

if [[ ! -d confluent-kafka-python ]]; then
git clone https://github.com/confluentinc/confluent-kafka-python
Expand All @@ -33,14 +32,14 @@ git checkout $python_branch

echo "Installing librdkafka $librdkafka_branch"
tools/bootstrap-librdkafka.sh --require-ssl $librdkafka_branch /usr
rm -rf tmp-build

echo "Installing interceptors"
tools/install-interceptors.sh
# echo "Installing interceptors"
# tools/install-interceptors.sh

venv=$HOME/venv
echo "Setting up virtualenv in $venv"
if [[ ! -d $venv ]]; then
virtualenv -p python3.6 $venv
python3 -m venv $venv
fi
source $venv/bin/activate

Expand All @@ -57,8 +56,6 @@ python -c "import confluent_kafka; print(confluent_kafka.version(), confluent_ka

deactivate

popd # $HOME

echo "All done, activate the virtualenv in $venv before running the client:"
echo "source $venv/bin/activate"

14 changes: 0 additions & 14 deletions tests/soak/ccloud.config.example

This file was deleted.

48 changes: 48 additions & 0 deletions tests/soak/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/bin/bash
set -e

DOCKER_REPOSITORY_DEFAULT=${DOCKER_REPOSITORY:-docker.io/library/njc-py-soak-tests}
NAMESPACE=njc-soak-tests
NOCACHE=${NOCACHE:---no-cache}

for var in LK_VERSION CKPY_VERSION CC_BOOSTRAP_SERVERS CC_USERNAME CC_PASSWORD TESTID \
DOCKER_REPOSITORY_DEFAULT; do
VAR_VALUE=$(eval echo \$$var)
if [ -z "$VAR_VALUE" ]; then
echo "env variable $var is required"
exit 1
fi
done

TAG=${LK_VERSION}-${CKPY_VERSION}

COMMAND="docker build . $NOCACHE --build-arg LK_VERSION=${LK_VERSION} \
--build-arg CKPY_VERSION=${CKPY_VERSION} \
-t ${DOCKER_REPOSITORY_DEFAULT}:${TAG}"
echo $COMMAND
$COMMAND

if [ ! -z "$DOCKER_REPOSITORY" ]; then
COMMAND="docker push ${DOCKER_REPOSITORY}:${TAG}"
echo $COMMAND
$COMMAND
fi

if [ "$(uname -p)" = "x86_64" ]; then
NODE_ARCH="amd64"
else
NODE_ARCH="arm64"
fi

COMMAND="helm upgrade --install njc-py-soak-tests-${TESTID} ./njc-py-soak-tests \
--set "cluster.bootstrapServers=${CC_BOOSTRAP_SERVERS}" \
--set "cluster.username=${CC_USERNAME}" \
--set "cluster.password=${CC_PASSWORD}" \
--set "image.repository=${DOCKER_REPOSITORY_DEFAULT}" \
--set "testid=${TESTID}" \
--set "fullnameOverride=njc-py-soak-tests-${TESTID}" \
--set "image.tag=${TAG}" \
--set "nodeSelector.kubernetes\\.io/arch=${NODE_ARCH}" \
--namespace "${NAMESPACE}" --create-namespace"
echo $COMMAND
$COMMAND
23 changes: 23 additions & 0 deletions tests/soak/njc-py-soak-tests/.helmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
24 changes: 24 additions & 0 deletions tests/soak/njc-py-soak-tests/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
apiVersion: v2
name: njc-py-soak-tests
description: A Helm chart for Kubernetes

# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application

# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.0

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.0.0"
1 change: 1 addition & 0 deletions tests/soak/njc-py-soak-tests/templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NJC Python soak tests installed!
62 changes: 62 additions & 0 deletions tests/soak/njc-py-soak-tests/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "njc-py-soak-tests.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "njc-py-soak-tests.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "njc-py-soak-tests.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "njc-py-soak-tests.labels" -}}
helm.sh/chart: {{ include "njc-py-soak-tests.chart" . }}
{{ include "njc-py-soak-tests.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "njc-py-soak-tests.selectorLabels" -}}
app.kubernetes.io/name: {{ include "njc-py-soak-tests.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Create the name of the service account to use
*/}}
{{- define "njc-py-soak-tests.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "njc-py-soak-tests.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
87 changes: 87 additions & 0 deletions tests/soak/njc-py-soak-tests/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "njc-py-soak-tests.fullname" . }}
labels:
{{- include "njc-py-soak-tests.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "njc-py-soak-tests.selectorLabels" . | nindent 6 }}
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "njc-py-soak-tests.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "njc-py-soak-tests.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
volumes:
- name: secret
secret:
secretName: {{ include "njc-py-soak-tests.fullname" $ }}-secret
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: NODEIP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.hostIP
- name: OTEL_METRICS_EXPORTER
value: "otlp"
- name: OTEL_RESOURCE_ATTRIBUTES
value: "service.name={{ include "njc-py-soak-tests.fullname" . }},service.version={{ .Values.image.tag | default .Chart.AppVersion }}"
- name: OTEL_TRACES_EXPORTER
value: "none"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "http://$(NODEIP):14317"
- name: OTEL_METRIC_EXPORT_INTERVAL
value: "10000"
- name: TESTID
value: "{{ .Values.testid }}"
volumeMounts:
- name: "secret"
mountPath: "/soaktests/confluent-kafka-python/ccloud.config"
subPath: ccloud.config
readOnly: true
# livenessProbe:
# httpGet:
# path: /
# port: http
# readinessProbe:
# httpGet:
# path: /
# port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
35 changes: 35 additions & 0 deletions tests/soak/njc-py-soak-tests/templates/secrets.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{{- range $nameSuffix, $values := .Values.secrets }}
---
apiVersion: v1
kind: Secret
metadata:
name: {{ include "njc-py-soak-tests.fullname" $ }}-{{ $nameSuffix }}
{{- with $values.annotations }}
annotations:
{{- range $key, $value := . }}
{{- printf "%s: %s" $key (tpl $value $ | quote) | nindent 4 }}
{{- end }}
{{- end }}
labels:
{{- range $key, $value := $values.labels }}
{{- printf "%s: %s" $key (tpl $value $ | quote) | nindent 4 }}
{{- end }}
type: {{ default "Opaque" $values.type }}
{{- with $values.data }}
data:
{{- toYaml . | nindent 2 }}
{{- end }}
stringData:
ccloud.config: |-
bootstrap.servers={{ $.Values.cluster.bootstrapServers }}
sasl.mechanisms=PLAIN
security.protocol=SASL_SSL
sasl.username={{ $.Values.cluster.username }}
sasl.password={{ $.Values.cluster.password }}
{{- $.Values.properties | nindent 4 -}}
{{- with $values.stringData }}
{{- range $key, $value := . }}
{{- printf "%s: %s" $key (tpl $value $ | quote) | nindent 2 }}
{{- end }}
{{- end }}
{{- end -}}
Loading