diff --git a/.drone/drone.yml b/.drone/drone.yml deleted file mode 100644 index e26f433..0000000 --- a/.drone/drone.yml +++ /dev/null @@ -1,168 +0,0 @@ -kind: pipeline -type: docker -name: linux-amd64 - -platform: - arch: amd64 - os: linux - -steps: -- name: test - image: docker.io/drycc/go-dev - pull: always - privileged: true - commands: - - make test - environment: - VERSION: ${DRONE_TAG:-latest}-linux-amd64 - DEV_REGISTRY: ${DEV_REGISTRY:-docker.io} - DRYCC_REGISTRY: ${DRYCC_REGISTRY:-docker.io} - when: - event: - - push - - tag - - pull_request - volumes: - - name: image_registries - path: /etc/containers/registries.conf - -- name: publish - image: docker.io/drycc/go-dev - pull: always - privileged: true - commands: - - echo $DOCKER_PASSWORD | docker login $DRYCC_REGISTRY --username $DOCKER_USERNAME --password-stdin - - make docker-build docker-immutable-push - environment: - VERSION: ${DRONE_TAG:-latest}-linux-amd64 - DEV_REGISTRY: - from_secret: dev_registry - DRYCC_REGISTRY: - from_secret: drycc_registry - DOCKER_USERNAME: - from_secret: docker_username - DOCKER_PASSWORD: - from_secret: docker_password - when: - event: - - push - - tag - volumes: - - name: image_registries - path: /etc/containers/registries.conf - -trigger: - event: - - push - - tag - - pull_request - -volumes: -- name: image_registries - host: - path: /etc/containers/registries.conf - ---- -kind: pipeline -type: docker -name: linux-arm64 - -platform: - arch: arm64 - os: linux - -steps: -- name: publish - image: docker.io/drycc/go-dev - pull: always - privileged: true - commands: - - echo $DOCKER_PASSWORD | docker login $DRYCC_REGISTRY --username $DOCKER_USERNAME --password-stdin - - make docker-build docker-immutable-push - environment: - VERSION: ${DRONE_TAG:-latest}-linux-arm64 - DEV_REGISTRY: - from_secret: dev_registry - DRYCC_REGISTRY: - from_secret: drycc_registry - DOCKER_USERNAME: - from_secret: docker_username - DOCKER_PASSWORD: - from_secret: docker_password - volumes: - - name: image_registries - path: /etc/containers/registries.conf - -trigger: - event: - - push - - tag - -volumes: -- name: image_registries - host: - path: /etc/containers/registries.conf - ---- -kind: pipeline -type: docker -name: manifest - -steps: -- name: generate manifest - image: docker.io/library/alpine - pull: always - commands: - - sed -i "s/docker.io/$${DRYCC_REGISTRY}/g" .drone/manifest.tmpl - environment: - DRYCC_REGISTRY: - from_secret: drycc_registry - -- name: publish - image: plugins/manifest - settings: - spec: .drone/manifest.tmpl - username: - from_secret: docker_username - password: - from_secret: docker_password - environment: - DEV_REGISTRY: - from_secret: dev_registry - DRYCC_REGISTRY: - from_secret: drycc_registry - -trigger: - event: - - push - - tag - -depends_on: -- linux-amd64 -- linux-arm64 - ---- -kind: pipeline -type: docker -name: chart - -steps: -- name: generate chart - image: docker.io/drycc/python-dev - commands: - - IMAGE_TAG=$([ ! -z $DRONE_TAG ] && echo \"${DRONE_TAG:1}\" || echo \"canary\") - - sed -i "s/image_tag:\ \"canary\"/image_tag:\ $IMAGE_TAG/g" charts/redis/values.yaml - - helm package charts/redis --version ${DRONE_TAG:-v1.0.0} - - curl -u $CHARTMUSEUM_USERNAME:$CHARTMUSEUM_PASSWORD -F chart=@redis-${DRONE_TAG:-v1.0.0}.tgz "$CHARTMUSEUM_API/api/$([ -z $DRONE_TAG ] && echo testing || echo stable)/charts" - environment: - CHARTMUSEUM_USERNAME: - from_secret: chartmuseum_username - CHARTMUSEUM_PASSWORD: - from_secret: chartmuseum_password - CHARTMUSEUM_API: - from_secret: chartmuseum_api - -trigger: - event: - - push - - tag diff --git a/.drone/manifest.tmpl b/.drone/manifest.tmpl deleted file mode 100644 index 4ee77d8..0000000 --- a/.drone/manifest.tmpl +++ /dev/null @@ -1,18 +0,0 @@ -image: docker.io/drycc/redis:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}canary{{/if}} -{{#if build.tags}} -tags: -{{#each build.tags}} - - {{this}} -{{/each}} -{{/if}} -manifests: - - - image: docker.io/drycc/redis:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-amd64 - platform: - architecture: amd64 - os: linux - - - image: docker.io/drycc/redis:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-arm64 - platform: - architecture: arm64 - os: linux diff --git a/.woodpecker/build-linux.yml b/.woodpecker/build-linux.yml new file mode 100644 index 0000000..ef4ecfd --- /dev/null +++ b/.woodpecker/build-linux.yml @@ -0,0 +1,35 @@ +matrix: + platform: + - linux/amd64 + - linux/arm64 + +labels: + type: exec + platform: ${platform} + +steps: +- name: publish-linux + image: bash + commands: + - export VERSION=$([ -z $CI_COMMIT_TAG ] && echo latest || echo $CI_COMMIT_TAG)-$(sed 's#/#-#g' <<< $CI_SYSTEM_PLATFORM) + - echo $CONTAINER_PASSWORD | podman login $DRYCC_REGISTRY --username $CONTAINER_USERNAME --password-stdin > /dev/null 2>&1 + - make podman-build podman-immutable-push + environment: + CODENAME: + from_secret: codename + DEV_REGISTRY: + from_secret: dev_registry + DRYCC_REGISTRY: + from_secret: drycc_registry + CONTAINER_USERNAME: + from_secret: container_username + CONTAINER_PASSWORD: + from_secret: container_password + when: + event: + - push + - tag + - cron + +depends_on: +- test-linux \ No newline at end of file diff --git a/.woodpecker/chart.yaml b/.woodpecker/chart.yaml new file mode 100644 index 0000000..cfcd3ec --- /dev/null +++ b/.woodpecker/chart.yaml @@ -0,0 +1,33 @@ +labels: + type: exec + platform: linux/amd64 + +steps: +- name: generate-chart + image: bash + commands: + - export VERSION=$(sed 's#v##' <<< $CI_COMMIT_TAG) + - export IMAGE_TAG=$([ ! -z $CI_COMMIT_TAG ] && echo \"$VERSION\" || echo \"canary\") + - export APP_VERSION=$([ -z $CI_COMMIT_TAG ] && echo $CI_COMMIT_SHA || echo $VERSION) + - export CHART_VERSION=$([ -z $CI_COMMIT_TAG ] && echo 1.0.0 || echo $VERSION) + - sed -i "s/imageTag:\ \"canary\"/imageTag:\ $IMAGE_TAG/g" charts/$${CI_REPO_NAME}/values.yaml + - helm package -u charts/$${CI_REPO_NAME} --version $CHART_VERSION --app-version $APP_VERSION + - echo $CONTAINER_PASSWORD | helm registry login $DRYCC_REGISTRY -u $CONTAINER_USERNAME --password-stdin + - helm push $${CI_REPO_NAME}-$CHART_VERSION.tgz oci://$DRYCC_REGISTRY/$([ -z $CI_COMMIT_TAG ] && echo charts-testing || echo charts) + environment: + DEV_REGISTRY: + from_secret: dev_registry + DRYCC_REGISTRY: + from_secret: drycc_registry + CONTAINER_USERNAME: + from_secret: container_username + CONTAINER_PASSWORD: + from_secret: container_password + when: + event: + - push + - tag + - cron + +depends_on: +- manifest diff --git a/.woodpecker/manifest.tmpl b/.woodpecker/manifest.tmpl new file mode 100644 index 0000000..8739535 --- /dev/null +++ b/.woodpecker/manifest.tmpl @@ -0,0 +1,18 @@ +image: registry.drycc.cc/drycc/{{project}}:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}canary{{/if}} +{{#if build.tags}} +tags: +{{#each build.tags}} + - {{this}} +{{/each}} +{{/if}} +manifests: + - + image: registry.drycc.cc/drycc/{{project}}:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-amd64 + platform: + architecture: amd64 + os: linux + - + image: registry.drycc.cc/drycc/{{project}}:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-arm64 + platform: + architecture: arm64 + os: linux diff --git a/.woodpecker/manifest.yml b/.woodpecker/manifest.yml new file mode 100644 index 0000000..19fc70c --- /dev/null +++ b/.woodpecker/manifest.yml @@ -0,0 +1,43 @@ +labels: + type: exec + platform: linux/amd64 + +steps: +- name: generate-manifest + image: bash + commands: + - sed -i "s/{{project}}/$${CI_REPO_NAME}/g" .woodpecker/manifest.tmpl + - sed -i "s/registry.drycc.cc/$${DRYCC_REGISTRY}/g" .woodpecker/manifest.tmpl + environment: + DRYCC_REGISTRY: + from_secret: drycc_registry + when: + event: + - tag + - push + - cron + +- name: publish-manifest + image: bash + commands: + - podman run --rm + -e PLUGIN_SPEC=.woodpecker/manifest.tmpl + -e PLUGIN_USERNAME=$CONTAINER_USERNAME + -e PLUGIN_PASSWORD=$CONTAINER_PASSWORD + -e DRONE_TAG=$CI_COMMIT_TAG + -v $(pwd):$(pwd) + -w $(pwd) + docker.io/plugins/manifest + environment: + CONTAINER_USERNAME: + from_secret: container_username + CONTAINER_PASSWORD: + from_secret: container_password + when: + event: + - tag + - push + - cron + +depends_on: +- build-linux diff --git a/.woodpecker/test-linux.yml b/.woodpecker/test-linux.yml new file mode 100644 index 0000000..194832f --- /dev/null +++ b/.woodpecker/test-linux.yml @@ -0,0 +1,24 @@ +matrix: + platform: + - linux/amd64 + - linux/arm64 + +labels: + type: exec + platform: ${platform} + +steps: +- name: test-linux + image: bash + commands: + - make test + environment: + CODENAME: + from_secret: codename + DEV_REGISTRY: + from_secret: dev_registry + when: + event: + - push + - tag + - cron diff --git a/Makefile b/Makefile index 03ff086..6c8d6c9 100644 --- a/Makefile +++ b/Makefile @@ -1,38 +1,36 @@ # Short name: Short name, following [a-zA-Z_], used all over the place. # Some uses for short name: -# - Docker image name +# - Container image name # - Kubernetes service, rc, pod, secret, volume names -SHORT_NAME := redis -DRYCC_REGISTY ?= ${DEV_REGISTRY} +SHORT_NAME := valkey +DRYCC_REGISTRY ?= ${DEV_REGISTRY} IMAGE_PREFIX ?= drycc PLATFORM ?= linux/amd64,linux/arm64 include versioning.mk -SHELL_SCRIPTS = $(wildcard _scripts/*.sh) rootfs/bin/boot +SHELL_SCRIPTS = $(wildcard _test/*.sh) rootfs/bin/valkey-start rootfs/scripts/* # The following variables describe the containerized development environment # and other build options DEV_ENV_IMAGE := ${DEV_REGISTRY}/drycc/go-dev -DEV_ENV_WORK_DIR := /go/src/${REPO_PATH} -DEV_ENV_CMD := docker run --rm -v ${CURDIR}:${DEV_ENV_WORK_DIR} -w ${DEV_ENV_WORK_DIR} ${DEV_ENV_IMAGE} -DEV_ENV_CMD_INT := docker run -it --rm -v ${CURDIR}:${DEV_ENV_WORK_DIR} -w ${DEV_ENV_WORK_DIR} ${DEV_ENV_IMAGE} +DEV_ENV_WORK_DIR := /opt/drycc/go/src/${REPO_PATH} +DEV_ENV_CMD := podman run --rm -v ${CURDIR}:${DEV_ENV_WORK_DIR} -w ${DEV_ENV_WORK_DIR} ${DEV_ENV_IMAGE} +DEV_ENV_CMD_INT := podman run -it --rm -v ${CURDIR}:${DEV_ENV_WORK_DIR} -w ${DEV_ENV_WORK_DIR} ${DEV_ENV_IMAGE} -all: docker-build docker-push +all: podman-build podman-push dev: ${DEV_ENV_CMD_INT} bash # For cases where we're building from local # We also alter the RC file to set the image name. -docker-build: - docker build ${DOCKER_BUILD_FLAGS} -t ${IMAGE} rootfs - docker tag ${IMAGE} ${MUTABLE_IMAGE} +podman-build: + podman build --build-arg CODENAME=${CODENAME} -t ${IMAGE} rootfs + podman tag ${IMAGE} ${MUTABLE_IMAGE} -docker-buildx: - docker buildx build --platform ${PLATFORM} ${DOCKER_BUILD_FLAGS} -t ${IMAGE} rootfs --push - -test: test-style +test: podman-build test-style + ./_test/test.sh ${VERSION} test-style: ${DEV_ENV_CMD} shellcheck $(SHELL_SCRIPTS) diff --git a/README.md b/README.md index a73cc65..ea5a0de 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,21 @@ -# Drycc Redis -[![Build Status](https://drone.drycc.cc/api/badges/drycc/redis/status.svg)](https://drone.drycc.cc/drycc/redis) +# Drycc Valkey +[![Build Status](https://woodpecker.drycc.cc/api/badges/drycc/valkey/status.svg)](https://woodpecker.drycc.cc/drycc/valkey) Drycc (pronounced DAY-iss) Workflow is an open source Platform as a Service (PaaS) that adds a developer-friendly layer to any [Kubernetes](http://kubernetes.io) cluster, making it easy to deploy and manage applications on your own servers. +## Usage + +Different components use different db, as follows: + +* `controller` use db 0 +* `passport` use db 1 +* `grafana` use db 2 +* `manager` use db 10 +* `helmbroker` use db 11 + +The above are the default configurations for each component. + ## Description -A Docker image for running standalone (not clustered) Redis on a Kubernetes cluster. +A Container image for running standalone (not clustered) Valkey on a Kubernetes cluster. [v2.18]: https://github.com/drycc/workflow/releases/tag/v2.18.0 diff --git a/_test/test.sh b/_test/test.sh new file mode 100755 index 0000000..574b989 --- /dev/null +++ b/_test/test.sh @@ -0,0 +1,140 @@ +#!/usr/bin/env bash + +VERSION="$1" + +CONTAINER_PROXY_NAME="valkey-benchmark-proxy" +CONTAINER_MASTER_NAME="valkey-benchmark-master" +CONTAINER_SLAVE1_NAME="valkey-benchmark-slave1" +CONTAINER_SLAVE2_NAME="valkey-benchmark-slave2" +DRYCC_VALKEY_PASSWORD=123456 + +function clean_before_exit { + # delay before exiting, so stdout/stderr flushes through the logging system + clean-valkey +} +trap clean_before_exit EXIT + +start-valkey-master() { + podman run -d \ + --rm \ + --env "REDISCLI_AUTH=$DRYCC_VALKEY_PASSWORD" \ + --env "DRYCC_VALKEY_PASSWORD=$DRYCC_VALKEY_PASSWORD" \ + --name "$CONTAINER_MASTER_NAME" \ + "registry.drycc.cc/drycc/valkey:$VERSION" \ + sleep infinity + + master_ip=$(podman exec "$CONTAINER_MASTER_NAME" hostname -i | tr -d '\r\n') + podman exec --env "DRYCC_VALKEY_SENTINEL=$master_ip" "$CONTAINER_MASTER_NAME" init-stack valkey-start server "$master_ip" & + podman exec --env "DRYCC_VALKEY_SENTINEL=$master_ip" "$CONTAINER_MASTER_NAME" init-stack valkey-start sentinel "$master_ip" & +} + +start-valkey-slave1() { + podman run -d \ + --rm \ + --env "REDISCLI_AUTH=$DRYCC_VALKEY_PASSWORD" \ + --env "DRYCC_VALKEY_PASSWORD=$DRYCC_VALKEY_PASSWORD" \ + --name "$CONTAINER_SLAVE1_NAME" \ + "registry.drycc.cc/drycc/valkey:$VERSION" \ + sleep infinity + + slave1_ip=$(podman exec -it "$CONTAINER_SLAVE1_NAME" hostname -i | tr -d '\r\n') + podman exec --env "DRYCC_VALKEY_SENTINEL=$master_ip" "$CONTAINER_SLAVE1_NAME" init-stack valkey-start server "$slave1_ip" & + podman exec --env "DRYCC_VALKEY_SENTINEL=$master_ip" "$CONTAINER_SLAVE1_NAME" init-stack valkey-start sentinel "$slave1_ip" & +} + +start-valkey-slave2() { + podman run -d \ + --rm \ + --env "REDISCLI_AUTH=$DRYCC_VALKEY_PASSWORD" \ + --env "DRYCC_VALKEY_PASSWORD=$DRYCC_VALKEY_PASSWORD" \ + --name "$CONTAINER_SLAVE2_NAME" \ + "registry.drycc.cc/drycc/valkey:$VERSION" \ + sleep infinity + + slave2_ip=$(podman exec -it "$CONTAINER_SLAVE2_NAME" hostname -i | tr -d '\r\n') + podman exec --env "DRYCC_VALKEY_SENTINEL=$master_ip" "$CONTAINER_SLAVE2_NAME" init-stack valkey-start server "$slave2_ip" & + podman exec --env "DRYCC_VALKEY_SENTINEL=$master_ip" "$CONTAINER_SLAVE2_NAME" init-stack valkey-start sentinel "$slave2_ip" & +} + +start-valkey-proxy() { + podman run -d \ + --rm \ + --env "REDISCLI_AUTH=$DRYCC_VALKEY_PASSWORD" \ + --env "DRYCC_VALKEY_PASSWORD=$DRYCC_VALKEY_PASSWORD" \ + --name "$CONTAINER_PROXY_NAME" \ + "registry.drycc.cc/drycc/valkey:$VERSION" \ + sleep infinity + + podman exec --env "DRYCC_VALKEY_SENTINEL=$master_ip" "$CONTAINER_PROXY_NAME" init-stack valkey-start proxy & +} + +clean-valkey() { + { + podman kill "$CONTAINER_PROXY_NAME" + podman kill "$CONTAINER_SLAVE1_NAME" + podman kill "$CONTAINER_SLAVE2_NAME" + podman kill "$CONTAINER_MASTER_NAME" + } >>/dev/null 2>&1 +} + +clean-valkey +start-valkey-master +start-valkey-slave1 +start-valkey-slave2 +start-valkey-proxy +echo "run valkey proxy benchmark..." +podman exec "$CONTAINER_PROXY_NAME" init-stack valkey-benchmark -p 16379 -a $DRYCC_VALKEY_PASSWORD + +echo "run valkey master benchmark..." +podman exec "$CONTAINER_MASTER_NAME" init-stack valkey-benchmark -a $DRYCC_VALKEY_PASSWORD + +echo "check slave all keys..." +KEYS=$(podman exec "$CONTAINER_MASTER_NAME" bash -c 'init-stack valkey-cli KEYS "*"') +if [[ "${KEYS}" == "" ]]; then + echo "error: there is no data from the database" + exit 1 +fi + +echo "check sentinel $CONTAINER_MASTER_NAME get master..." +MASTER=$(podman exec "$CONTAINER_MASTER_NAME" init-stack valkey-cli -p 26379 sentinel get-master-addr-by-name drycc) +if [[ "${MASTER}" == "" ]]; then + echo "error: unable to obtain master information" + exit 1 +fi + +echo "check sentinel $CONTAINER_SLAVE1_NAME get master..." +MASTER=$(podman exec "$CONTAINER_SLAVE1_NAME" init-stack valkey-cli -p 26379 sentinel get-master-addr-by-name drycc) +if [[ "${MASTER}" == "" ]]; then + echo "error: unable to obtain master information" + exit 1 +fi + +echo "check sentinel $CONTAINER_SLAVE2_NAME get master..." +MASTER=$(podman exec "$CONTAINER_SLAVE2_NAME" init-stack valkey-cli -p 26379 sentinel get-master-addr-by-name drycc) +if [[ "${MASTER}" == "" ]]; then + echo "error: unable to obtain master information" + exit 1 +fi + +echo "check sentinel $CONTAINER_MASTER_NAME get slaves..." +SLAVES=$(podman exec "$CONTAINER_MASTER_NAME" init-stack valkey-cli -p 26379 sentinel replicas drycc) +if [[ "${SLAVES}" == "" ]]; then + echo "error: unable to obtain slaves information" + exit 1 +fi + +echo "check sentinel $CONTAINER_SLAVE1_NAME get slaves..." +SLAVES=$(podman exec "$CONTAINER_SLAVE1_NAME" init-stack valkey-cli -p 26379 sentinel replicas drycc) +if [[ "${SLAVES}" == "" ]]; then + echo "error: unable to obtain slaves information" + exit 1 +fi + +echo "check sentinel $CONTAINER_SLAVE2_NAME get slaves..." +SLAVES=$(podman exec "$CONTAINER_SLAVE2_NAME" init-stack valkey-cli -p 26379 sentinel replicas drycc) +if [[ "${SLAVES}" == "" ]]; then + echo "error: unable to obtain slaves information" + exit 1 +fi + +echo "all test ok..." diff --git a/charts/redis/Chart.yaml b/charts/redis/Chart.yaml deleted file mode 100644 index ae744a4..0000000 --- a/charts/redis/Chart.yaml +++ /dev/null @@ -1,7 +0,0 @@ -name: redis -home: https://github.com/drycc/redis -version: v1.0.0 -description: A Redis database for use inside a Kubernetes cluster. -maintainers: - - name: Drycc Team - email: engineering@drycc.com diff --git a/charts/redis/templates/redis-secret-creds.yaml b/charts/redis/templates/redis-secret-creds.yaml deleted file mode 100644 index eb4dd1f..0000000 --- a/charts/redis/templates/redis-secret-creds.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: redis-creds - labels: - app: drycc-redis - heritage: drycc - annotations: - "helm.sh/hook": pre-install -data: - {{ if eq .Values.global.redis_location "on-cluster"}} - password: {{ randAlphaNum 32 | b64enc }} - {{ else if eq .Values.global.redis_location "off-cluster"}} - addrs: {{ .Values.addrs | b64enc }} - password: {{ .Values.password | b64enc }} - {{ end }} diff --git a/charts/redis/templates/redis-statefulset.yaml b/charts/redis/templates/redis-statefulset.yaml deleted file mode 100644 index 8d69964..0000000 --- a/charts/redis/templates/redis-statefulset.yaml +++ /dev/null @@ -1,45 +0,0 @@ -{{- if eq .Values.global.redis_location "on-cluster" }} -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: drycc-redis - labels: - heritage: drycc - annotations: - component.drycc.cc/version: {{ .Values.image_tag }} -spec: - serviceName: drycc-redis - replicas: {{ .Values.replicas }} - selector: - matchLabels: - app: drycc-redis - template: - metadata: - labels: - app: drycc-redis - spec: - containers: - - name: drycc-redis - image: {{.Values.image_registry}}/{{.Values.image_org}}/redis:{{ .Values.image_tag }} - imagePullPolicy: {{ .Values.image_pull_policy }} -{{- if or (.Values.limits_cpu) (.Values.limits_memory)}} - resources: - limits: -{{- if (.Values.limits_cpu) }} - cpu: {{.Values.limits_cpu}} -{{- end}} -{{- if (.Values.limits_memory) }} - memory: {{.Values.limits_memory}} -{{- end}} -{{- end}} - command: ["/bin/boot", "--port", "6379"] - ports: - - containerPort: 6379 - volumeMounts: - - name: redis-creds - mountPath: /var/run/secrets/drycc/redis/creds - volumes: - - name: redis-creds - secret: - secretName: redis-creds -{{- end }} diff --git a/charts/redis/templates/redis-svc.yaml b/charts/redis/templates/redis-svc.yaml deleted file mode 100644 index 6d32a80..0000000 --- a/charts/redis/templates/redis-svc.yaml +++ /dev/null @@ -1,14 +0,0 @@ -{{- if eq .Values.global.redis_location "on-cluster" }} -apiVersion: v1 -kind: Service -metadata: - name: drycc-redis - labels: - heritage: drycc -spec: - clusterIP: None - selector: - app: drycc-redis - ports: - - port: 6379 -{{- end }} diff --git a/charts/redis/values.yaml b/charts/redis/values.yaml deleted file mode 100644 index a472306..0000000 --- a/charts/redis/values.yaml +++ /dev/null @@ -1,21 +0,0 @@ -image_org: "drycc" -image_pull_policy: "Always" -image_tag: "canary" -image_registry: "docker.io" -# limits_cpu: "100m" -# limits_memory: "50Mi" - -global: - # Set the location of Workflow's redis instance - # - # Valid values are: - # - on-cluster: Run Redis within the Kubernetes cluster - # - off-cluster: Run Redis outside the Kubernetes cluster (configure in redis section) - redis_location: "on-cluster" - -# The following parameters are configured only when using an on-cluster Redis instance -replicas: 1 - -# The following parameters are configured only when using an off-cluster Redis instance -addrs: "" # A list of clusters: "127.0.0.1:7001/1,127.0.0.2:7002/1" -password: "redis password" # "" == no password diff --git a/charts/valkey/Chart.yaml b/charts/valkey/Chart.yaml new file mode 100644 index 0000000..5e7e12d --- /dev/null +++ b/charts/valkey/Chart.yaml @@ -0,0 +1,13 @@ +name: valkey +home: https://github.com/drycc/valkey +apiVersion: v2 +appVersion: 1.0.0 +dependencies: + - name: common + repository: oci://registry.drycc.cc/charts + version: ~1.1.2 +description: A Valkey database for use inside a Kubernetes cluster. +maintainers: + - name: Drycc Team + email: engineering@drycc.com +version: v1.0.0 diff --git a/charts/valkey/templates/valkey-secret-creds.yaml b/charts/valkey/templates/valkey-secret-creds.yaml new file mode 100644 index 0000000..33c81d2 --- /dev/null +++ b/charts/valkey/templates/valkey-secret-creds.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: valkey-creds + labels: + app: drycc-valkey + heritage: drycc +data: + password: {{ include "common.secrets.lookup" (dict "secret" "valkey-creds" "key" "password" "defaultValue" (randAlphaNum 32) "context" $) }} diff --git a/charts/valkey/templates/valkey-statefulset.yaml b/charts/valkey/templates/valkey-statefulset.yaml new file mode 100644 index 0000000..be223df --- /dev/null +++ b/charts/valkey/templates/valkey-statefulset.yaml @@ -0,0 +1,290 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: drycc-valkey + labels: + heritage: drycc + annotations: + component.drycc.cc/version: {{ .Values.imageTag }} +spec: + serviceName: drycc-valkey + replicas: {{ .Values.replicas }} + selector: + matchLabels: + app: drycc-valkey + template: + metadata: + labels: {{- include "common.labels.standard" . | nindent 8 }} + app: drycc-valkey + spec: + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAffinityPreset.type "component" "" "extraMatchLabels" .Values.podAffinityPreset.extraMatchLabels "topologyKey" "" "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAntiAffinityPreset.type "component" "" "extraMatchLabels" .Values.podAntiAffinityPreset.extraMatchLabels "topologyKey" "" "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.nodeAffinityPreset.type "key" .Values.nodeAffinityPreset.key "values" .Values.nodeAffinityPreset.values ) | nindent 10 }} + containers: + - name: drycc-valkey-proxy + image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/valkey:{{ .Values.imageTag }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 10 }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 10 }} + {{- else }} + args: + - valkey-start + - proxy + {{- end }} + env: + - name: DRYCC_VALKEY_PASSWORD + valueFrom: + secretKeyRef: + name: valkey-creds + key: password + - name: DRYCC_VALKEY_SENTINEL + value: drycc-valkey + ports: + - containerPort: 16379 + {{- with index .Values "proxy" "resources" }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + startupProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 22 + tcpSocket: + port: 16379 + livenessProbe: + initialDelaySeconds: 20 + periodSeconds: 5 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + tcpSocket: + port: 16379 + readinessProbe: + initialDelaySeconds: 20 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + tcpSocket: + port: 16379 + - name: drycc-valkey-server + image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/valkey:{{ .Values.imageTag }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 10 }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 10 }} + {{- else }} + args: + - valkey-start + - server + - $(POD_NAME).drycc-valkey + {{- end }} + ports: + - containerPort: 6379 + {{- with index .Values "server" "resources" }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: DRYCC_VALKEY_SENTINEL + value: drycc-valkey + - name: DRYCC_VALKEY_PASSWORD + valueFrom: + secretKeyRef: + name: valkey-creds + key: password + startupProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 22 + tcpSocket: + port: 6379 + livenessProbe: + initialDelaySeconds: 20 + periodSeconds: 5 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + exec: + command: + - init-stack + - sh + - -c + - /scripts/ping_liveness_local.sh 5 + readinessProbe: + initialDelaySeconds: 20 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + exec: + command: + - init-stack + - sh + - -c + - /scripts/ping_readiness_local.sh 1 + {{- if .Values.persistence.enabled }} + volumeMounts: + - name: valkey-data + mountPath: /data + {{- end }} + lifecycle: + preStop: + exec: + command: + - init-stack + - /bin/bash + - -c + - /scripts/prestop-valkey.sh + - name: drycc-valkey-sentinel + image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/valkey:{{ .Values.imageTag }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 10 }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 10 }} + {{- else }} + args: + - valkey-start + - sentinel + - $(POD_NAME).drycc-valkey + {{- end }} + ports: + - containerPort: 26379 + {{- with index .Values "sentinel" "resources" }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: DRYCC_VALKEY_SENTINEL + value: drycc-valkey + - name: DRYCC_VALKEY_PASSWORD + valueFrom: + secretKeyRef: + name: valkey-creds + key: password + startupProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 22 + tcpSocket: + port: 26379 + livenessProbe: + initialDelaySeconds: 20 + periodSeconds: 5 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + exec: + command: + - init-stack + - sh + - -c + - /scripts/ping_sentinel.sh 5 + readinessProbe: + initialDelaySeconds: 20 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + exec: + command: + - init-stack + - sh + - -c + - /scripts/ping_sentinel.sh 1 + {{- if .Values.persistence.enabled }} + volumeMounts: + - name: valkey-data + mountPath: /data + {{- end }} + lifecycle: + preStop: + exec: + command: + - init-stack + - /bin/bash + - -c + - /scripts/prestop-sentinel.sh + - name: drycc-valkey-metrics + image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/valkey:{{ .Values.imageTag }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 10 }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 10 }} + {{- else }} + command: + - init-stack + args: + - redis_exporter + {{- end }} + ports: + - containerPort: 9121 + env: + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: valkey-creds + key: password + startupProbe: + initialDelaySeconds: 10 + tcpSocket: + port: 9121 + periodSeconds: 10 + timeoutSeconds: 1 + failureThreshold: 15 + successThreshold: 1 + livenessProbe: + initialDelaySeconds: 5 + httpGet: + path: / + port: 9121 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + readinessProbe: + initialDelaySeconds: 5 + httpGet: + path: / + port: 9121 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + securityContext: + fsGroup: 1001 + runAsGroup: 1001 + runAsUser: 1001 + {{- if .Values.persistence.enabled }} + volumeClaimTemplates: + - metadata: + name: valkey-data + spec: + accessModes: [ "ReadWriteOnce" ] + {{- if .Values.persistence.storageClass }} + {{- if (eq "-" .Values.persistence.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: "{{ .Values.persistence.storageClass }}" + {{- end }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{- end }} diff --git a/charts/valkey/templates/valkey-svc.yaml b/charts/valkey/templates/valkey-svc.yaml new file mode 100644 index 0000000..c38af8c --- /dev/null +++ b/charts/valkey/templates/valkey-svc.yaml @@ -0,0 +1,35 @@ +apiVersion: v1 +kind: Service +metadata: + name: drycc-valkey + annotations: + prometheus.io/path: /metrics + prometheus.io/port: "9121" + prometheus.io/scrape: "true" + {{- with .Values.service.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + heritage: drycc +spec: + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: proxy + port: 16379 + targetPort: 16379 + protocol: TCP + - name: server + port: 6379 + targetPort: 6379 + protocol: TCP + - name: metrics + port: 9121 + targetPort: 9121 + protocol: TCP + - name: sentinel + port: 26379 + targetPort: 26379 + protocol: TCP + selector: + app: drycc-valkey diff --git a/charts/valkey/values.yaml b/charts/valkey/values.yaml new file mode 100644 index 0000000..bf19ca1 --- /dev/null +++ b/charts/valkey/values.yaml @@ -0,0 +1,76 @@ +imageOrg: "drycc" +imagePullPolicy: "Always" +imageTag: "canary" +imageRegistry: "registry.drycc.cc" + +## Enable diagnostic mode +## +diagnosticMode: + ## @param diagnosticMode.enabled Enable diagnostic mode (all probes will be disabled and the command will be overridden) + ## + enabled: false + ## @param diagnosticMode.command Command to override all containers + ## + command: + - sleep + ## @param diagnosticMode.args Args to override all containers + ## + args: + - infinity + +nodeAffinityPreset: + key: "drycc.cc/node" + type: "soft" + values: + - "true" + +podAffinityPreset: + type: "" + extraMatchLabels: + security: "drycc-security" + +podAntiAffinityPreset: + type: "soft" + extraMatchLabels: + app: "drycc-valkey" + +# The following parameters are configured only when using an on-cluster Valkey instance +replicas: 3 + +# Service +service: + # Provide any additional service annotations + annotations: {} + +proxy: + resources: {} + # limits: + # cpu: 200m + # memory: 50Mi + # requests: + # cpu: 100m + # memory: 30Mi + +server: + resources: {} + # limits: + # cpu: 200m + # memory: 50Mi + # requests: + # cpu: 100m + # memory: 30Mi + +sentinel: + resources: {} + # limits: + # cpu: 200m + # memory: 50Mi + # requests: + # cpu: 100m + # memory: 30Mi + +# GCP PDs and EBS volumes are supported only +persistence: + enabled: false # Set to true to enable persistence + size: 5Gi + storageClass: "" diff --git a/rootfs/Dockerfile b/rootfs/Dockerfile index 723858d..6068bee 100644 --- a/rootfs/Dockerfile +++ b/rootfs/Dockerfile @@ -1,10 +1,39 @@ -FROM docker.io/library/redis:6-alpine +ARG CODENAME +FROM registry.drycc.cc/drycc/base:${CODENAME} -COPY . / +ARG DRYCC_UID=1001 \ + DRYCC_GID=1001 \ + DRYCC_HOME_DIR=/data \ + VALKEY_VERSION="9.0.0" \ + VALKEY_SENTINEL_PROXY_VERSION="2.0.1" \ + REDIS_EXPORTER_VERSION="1.80.1" -RUN chown -R redis:redis /etc/redis +RUN groupadd drycc --gid ${DRYCC_GID} \ + && useradd drycc -u ${DRYCC_UID} -g ${DRYCC_GID} -s /bin/bash -m -d ${DRYCC_HOME_DIR} -USER redis +COPY scripts /scripts +COPY etc/valkey /etc/valkey +COPY bin/valkey-start /bin/valkey-start -CMD ["/bin/boot"] -EXPOSE 6379 +RUN install-stack valkey ${VALKEY_VERSION} \ + && install-stack valkey-sentinel-proxy ${VALKEY_SENTINEL_PROXY_VERSION} \ + && install-stack redis_exporter $REDIS_EXPORTER_VERSION \ + && rm -rf \ + /usr/share/doc \ + /usr/share/man \ + /usr/share/info \ + /usr/share/locale \ + /var/lib/apt/lists/* \ + /var/log/* \ + /var/cache/debconf/* \ + /etc/systemd \ + /lib/lsb \ + /lib/udev \ + /usr/lib/`echo $(uname -m)`-linux-gnu/gconv/IBM* \ + /usr/lib/`echo $(uname -m)`-linux-gnu/gconv/EBC* \ + && mkdir -p /usr/share/man/man{1..8} \ + && chown -R ${DRYCC_UID}:${DRYCC_GID} /etc/valkey ${DRYCC_HOME_DIR} + +USER ${DRYCC_UID} +WORKDIR ${DRYCC_HOME_DIR} +EXPOSE 6379 9121 26379 diff --git a/rootfs/bin/boot b/rootfs/bin/boot deleted file mode 100755 index f0b4ab6..0000000 --- a/rootfs/bin/boot +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env sh - -set -eof pipefail - -REDIS_CONFIG_FILE=/etc/redis/redis.conf - -# Set password -REDIS_PASSWORD_FILE=/var/run/secrets/drycc/redis/creds/password -if [ -e $REDIS_PASSWORD_FILE ]; then - REDIS_PASSWORD="$(cat /var/run/secrets/drycc/redis/creds/password)" - if [ -n "$REDIS_PASSWORD" ]; then - echo "requirepass $REDIS_PASSWORD" >> $REDIS_CONFIG_FILE - fi -fi - -exec redis-server $REDIS_CONFIG_FILE "$@" diff --git a/rootfs/bin/valkey-start b/rootfs/bin/valkey-start new file mode 100755 index 0000000..d8204d8 --- /dev/null +++ b/rootfs/bin/valkey-start @@ -0,0 +1,126 @@ +#!/usr/bin/env bash + +# shellcheck disable=SC1091 +. /scripts/init.sh + +print_usage() { + echo "Valid commands for valkey-start:" + echo "" + echo "proxy start valkey proxy" + echo "server start valkey server" + echo "sentinel start valkey sentinel" + echo "" + echo "Such as 'valkey-start server valkey.valkey.svc' to start valkey server." +} + +remove_in_file() { + local filename="${1:?filename is required}" + local match_regex="^\s*${2:?match regex is required} .*" + sed -i "/$match_regex/d" "$filename" +} + +get_master_info() { + export REDISCLI_AUTH=${DRYCC_VALKEY_PASSWORD} + command="valkey-cli -h ${DRYCC_VALKEY_SENTINEL} -p ${SENTINEL_PORT} sentinel get-master-addr-by-name drycc 2>>/dev/null" + eval "$command" +} + +wait_for_valkey() { + local host="${1:?host is required}" + local port="${2:?port is required}" + local retries="${3:-30}" + log "Waiting for valkey (${host}:${port}) to be ready..." + for ((i = 1; i <= retries; i += 1)); do + if valkey-cli -h "$host" -p "$port" ping 2>/dev/null | grep -q PONG; then + log "Valkey (${host}:${port}) is ready." + return 0 + fi + sleep 2s + done + error "Valkey (${host}:${port}) did not become ready in time" +} + +start_valkey_proxy() { + wait_for_valkey "${DRYCC_VALKEY_SENTINEL}" "${SENTINEL_PORT}" + exec valkey-sentinel-proxy \ + --listen=:16379 \ + --master=drycc \ + --max-procs=4 \ + --sentinel-addr="${DRYCC_VALKEY_SENTINEL}":26379 \ + --sentinel-pass="${DRYCC_VALKEY_PASSWORD}" +} + +start_valkey_server() { + announce_ip="$1" + VALKEY_CONFIG_FILE=/data/server/valkey.conf + if [ ! -f ${VALKEY_CONFIG_FILE} ]; then + cp -rf /etc/valkey/valkey-default.conf ${VALKEY_CONFIG_FILE} + fi + + # Clean old + remove_in_file ${VALKEY_CONFIG_FILE} masterauth + remove_in_file ${VALKEY_CONFIG_FILE} requirepass + remove_in_file ${VALKEY_CONFIG_FILE} replicaof + remove_in_file ${VALKEY_CONFIG_FILE} replica-announce-ip + remove_in_file ${VALKEY_CONFIG_FILE} replica-announce-port + { + printf "\nmasterauth %s" "${DRYCC_VALKEY_PASSWORD}" + printf "\nrequirepass %s" "${DRYCC_VALKEY_PASSWORD}" + printf "\nreplica-announce-ip %s" "${announce_ip}" + printf "\nreplica-announce-port %s" "${SERVER_PORT}" + } >> ${VALKEY_CONFIG_FILE} + # Set server slaveof + if get_master_info; then + # shellcheck disable=SC2207 + master_info=($(get_master_info)) + if [ "${master_info[0]}" != "${announce_ip}" ]; then + printf "\nreplicaof %s %s" "${master_info[0]}" "${master_info[1]}" >> "${VALKEY_CONFIG_FILE}" + fi + fi + exec valkey-server "${VALKEY_CONFIG_FILE}" +} + +start_valkey_sentinel() { + announce_ip="$1" + VALKEY_SENTINEL_CONFIG_FILE=/data/sentinel/valkey-sentinel.conf + if [ ! -f ${VALKEY_SENTINEL_CONFIG_FILE} ]; then + cp -rf /etc/valkey/valkey-sentinel-default.conf ${VALKEY_SENTINEL_CONFIG_FILE} + fi + + # Clean old + remove_in_file ${VALKEY_SENTINEL_CONFIG_FILE} masterauth + remove_in_file ${VALKEY_SENTINEL_CONFIG_FILE} requirepass + remove_in_file ${VALKEY_SENTINEL_CONFIG_FILE} primaryauth + remove_in_file ${VALKEY_SENTINEL_CONFIG_FILE} "sentinel auth-pass" + remove_in_file ${VALKEY_SENTINEL_CONFIG_FILE} "sentinel announce-ip" + remove_in_file ${VALKEY_SENTINEL_CONFIG_FILE} "sentinel announce-port" + remove_in_file ${VALKEY_SENTINEL_CONFIG_FILE} "sentinel monitor" + + # Set sentinel config + { + printf "\nmasterauth %s" "${DRYCC_VALKEY_PASSWORD}" + printf "\nrequirepass %s" "${DRYCC_VALKEY_PASSWORD}" + printf "\nprimaryauth %s" "${DRYCC_VALKEY_PASSWORD}" + printf "\nsentinel auth-pass drycc %s" "${DRYCC_VALKEY_PASSWORD}" + printf "\nsentinel announce-ip %s" "${announce_ip}" + printf "\nsentinel announce-port %s" "${SENTINEL_PORT}" + } >> ${VALKEY_SENTINEL_CONFIG_FILE} + + # Set monitor + if get_master_info; then + # shellcheck disable=SC2207 + master_info=($(get_master_info)) + printf "\nsentinel monitor drycc %s %s 2" "${master_info[0]}" "${master_info[1]}" >> ${VALKEY_SENTINEL_CONFIG_FILE} + else + printf "\nsentinel monitor drycc %s %s 2" "${announce_ip}" "${SERVER_PORT}" >> ${VALKEY_SENTINEL_CONFIG_FILE} + fi + exec valkey-server $VALKEY_SENTINEL_CONFIG_FILE --sentinel +} + +command="$1" +if [[ ${command} == "proxy" || ${command} == "server" || ${command} == "sentinel" ]]; then + "start_valkey_$command" "$2" +else + print_usage + exit 1 +fi diff --git a/rootfs/etc/redis/redis.conf b/rootfs/etc/redis/redis.conf deleted file mode 100644 index 5b7a8d9..0000000 --- a/rootfs/etc/redis/redis.conf +++ /dev/null @@ -1,3 +0,0 @@ -daemonize no -bind 0.0.0.0 -logfile "" diff --git a/rootfs/etc/valkey/valkey-default.conf b/rootfs/etc/valkey/valkey-default.conf new file mode 100644 index 0000000..0b51917 --- /dev/null +++ b/rootfs/etc/valkey/valkey-default.conf @@ -0,0 +1,13 @@ +daemonize no +bind 0.0.0.0 +logfile "" +dir "/data/server" + +save 900 1 +save 300 10 +save 60 1000 + +appendonly yes +auto-aof-rewrite-percentage 100 +auto-aof-rewrite-min-size 64mb +appendfsync everysec diff --git a/rootfs/etc/valkey/valkey-sentinel-default.conf b/rootfs/etc/valkey/valkey-sentinel-default.conf new file mode 100644 index 0000000..429f3b6 --- /dev/null +++ b/rootfs/etc/valkey/valkey-sentinel-default.conf @@ -0,0 +1,10 @@ +daemonize no +bind 0.0.0.0 +logfile "" +dir "/data/sentinel" + +sentinel resolve-hostnames yes +sentinel announce-hostnames yes +sentinel down-after-milliseconds drycc 60000 +sentinel failover-timeout drycc 180000 +sentinel parallel-syncs drycc 1 diff --git a/rootfs/scripts/init.sh b/rootfs/scripts/init.sh new file mode 100755 index 0000000..e9ee373 --- /dev/null +++ b/rootfs/scripts/init.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# Constants +RESET='\033[0m' +RED='\033[38;5;1m' +MAGENTA='\033[38;5;5m' +CYAN='\033[38;5;6m' + +log() { + # 'is_boolean_yes' is defined in libvalidations.sh, but depends on this file so we cannot source it + local bool="${DRYCC_QUIET:-false}" + # comparison is performed without regard to the case of alphabetic characters + shopt -s nocasematch + if ! [[ "$bool" = 1 || "$bool" =~ ^(yes|true)$ ]]; then + printf "%b\\n" "${CYAN}${MODULE:-} ${MAGENTA}$(date "+%T.%2N ")${RESET}${*}" >&2 + fi +} + +error() { + log "${RED}ERROR${RESET} ==> ${*}" + exit 1 +} + +######################## +# Retries a command a given number of times +# Arguments: +# $1 - cmd (as a string) +# $2 - max retries. Default: 12 +# $3 - sleep between retries (in seconds). Default: 5 +# Returns: +# Boolean +######################### +retry_while() { + local cmd="${1:?cmd is missing}" + local retries="${2:-12}" + local sleep_time="${3:-5}" + local return_value=1 + + read -r -a command <<<"$cmd" + for ((i = 1; i <= retries; i += 1)); do + "${command[@]}" && return_value=0 && break + sleep "$sleep_time" + done + return $return_value +} + +init_default_env() { + if [[ -z "$DRYCC_VALKEY_SENTINEL" ]]; then + error "DRYCC_VALKEY_SENTINEL cannot be empty" + fi + if [[ -z "$DRYCC_VALKEY_PASSWORD" ]]; then + error "DRYCC_VALKEY_PASSWORD cannot be empty" + fi + mkdir -p /data/{server,sentinel} + SERVER_PORT=${SERVER_PORT:-6379} + SENTINEL_PORT=${SENTINEL_PORT:-26379} + REDISCLI_AUTH="$DRYCC_VALKEY_PASSWORD" + export SERVER_PORT SENTINEL_PORT REDISCLI_AUTH +} + +init_default_env diff --git a/rootfs/scripts/ping_liveness_local.sh b/rootfs/scripts/ping_liveness_local.sh new file mode 100755 index 0000000..baf72d2 --- /dev/null +++ b/rootfs/scripts/ping_liveness_local.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# shellcheck disable=SC1091 +. /scripts/init.sh + +response=$( + timeout -s 3 "$1" \ + valkey-cli \ + -h localhost \ + -p "${SERVER_PORT}" \ + ping +) +if [ "$?" -eq "124" ]; then + echo "Timed out" + exit 1 +fi +responseFirstWord=$(echo "$response" | head -n1 | awk '{print $1;}') +if [ "$response" != "PONG" ] && [ "$responseFirstWord" != "LOADING" ] && [ "$responseFirstWord" != "MASTERDOWN" ]; then + echo "$response" + exit 1 +fi \ No newline at end of file diff --git a/rootfs/scripts/ping_readiness_local.sh b/rootfs/scripts/ping_readiness_local.sh new file mode 100755 index 0000000..df10bac --- /dev/null +++ b/rootfs/scripts/ping_readiness_local.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# shellcheck disable=SC1091 +. /scripts/init.sh + +response=$( + timeout -s 3 "$1" \ + valkey-cli \ + -h localhost \ + -p "${SERVER_PORT}" \ + ping +) +if [ "$?" -eq "124" ]; then + echo "Timed out" + exit 1 +fi +if [ "$response" != "PONG" ]; then + echo "$response" + exit 1 +fi \ No newline at end of file diff --git a/rootfs/scripts/ping_sentinel.sh b/rootfs/scripts/ping_sentinel.sh new file mode 100755 index 0000000..159f4c8 --- /dev/null +++ b/rootfs/scripts/ping_sentinel.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# shellcheck disable=SC1091 +. /scripts/init.sh + +response=$( + timeout -s 3 "$1" \ + valkey-cli \ + -h localhost \ + -p "${SENTINEL_PORT}" \ + ping +) +if [ "$?" -eq "124" ]; then + echo "Timed out" + exit 1 +fi +if [ "$response" != "PONG" ]; then + echo "$response" + exit 1 +fi diff --git a/rootfs/scripts/prestop-sentinel.sh b/rootfs/scripts/prestop-sentinel.sh new file mode 100755 index 0000000..0bb158b --- /dev/null +++ b/rootfs/scripts/prestop-sentinel.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# shellcheck disable=SC1091 +. /scripts/init.sh + +run_sentinel_command() { + valkey-cli -h "$DRYCC_VALKEY_SENTINEL" -p "${SENTINEL_PORT}" sentinel "$@" +} + +failover_finished() { + # shellcheck disable=SC2207 + SENTINEL_INFO=($(run_sentinel_command get-master-addr-by-name drycc)) + VALKEY_MASTER_HOST="${SENTINEL_INFO[0]}" + [[ "$VALKEY_MASTER_HOST" != "$(hostname -I | xargs)" ]] +} + +if ! failover_finished; then + echo "I am the master pod and you are stopping me. Starting sentinel failover" + # if I am the master, issue a command to failover once and then wait for the failover to finish + run_sentinel_command failover drycc + if retry_while "failover_finished" 30 1; then + echo "Master has been successfuly failed over to a different pod." + exit 0 + else + echo "Master failover failed" + exit 1 + fi +else + exit 0 +fi \ No newline at end of file diff --git a/rootfs/scripts/prestop-valkey.sh b/rootfs/scripts/prestop-valkey.sh new file mode 100755 index 0000000..275cee6 --- /dev/null +++ b/rootfs/scripts/prestop-valkey.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# shellcheck disable=SC1091 +. /scripts/init.sh + +run_valkey_command() { + valkey-cli -h localhost -p "${SERVER_PORT}" "$@" +} + +failover_finished() { + VALKEY_ROLE=$(run_valkey_command role | head -1) + [[ "$VALKEY_ROLE" != "master" ]] +} + +if ! failover_finished; then + echo "Waiting for sentinel to run failover for up to {{ sub .Values.sentinel.terminationGracePeriodSeconds 10 }}s" + retry_while "failover_finished" 30 1 +else + exit 0 +fi diff --git a/versioning.mk b/versioning.mk index fd4a488..10e3d7b 100644 --- a/versioning.mk +++ b/versioning.mk @@ -10,13 +10,13 @@ info: @echo "Immutable tag: ${IMAGE}" @echo "Mutable tag: ${MUTABLE_IMAGE}" -.PHONY: docker-push -docker-push: docker-immutable-push docker-mutable-push +.PHONY: podman-push +podman-push: podman-immutable-push podman-mutable-push -.PHONY: docker-immutable-push -docker-immutable-push: - docker push ${IMAGE} +.PHONY: podman-immutable-push +podman-immutable-push: + podman push ${IMAGE} -.PHONY: docker-mutable-push -docker-mutable-push: - docker push ${MUTABLE_IMAGE} +.PHONY: podman-mutable-push +podman-mutable-push: + podman push ${MUTABLE_IMAGE}