Skip to content

Commit aac9149

Browse files
committed
Add Makefile scale test targets; drop demo worker sample
scale-load, scale-observe, and scale-load-clear drive Redis backlog via kubectl exec into deploy/redis
1 parent c12035e commit aac9149

9 files changed

Lines changed: 80 additions & 54 deletions

File tree

.custom-gcl.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
version: v2.11.4
2+
plugins:
3+
- module: "sigs.k8s.io/logtools"
4+
import: "sigs.k8s.io/logtools/logcheck/gclplugin"
5+
version: v0.10.1

Makefile

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,15 @@
1-
# Image URL to use all building/pushing image targets
21
IMG ?= controller:latest
3-
# YEAR defines the year value used for substituting the YEAR placeholder in the boilerplate header.
2+
43
YEAR ?= $(shell date +%Y)
54

6-
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
75
ifeq (,$(shell go env GOBIN))
86
GOBIN=$(shell go env GOPATH)/bin
97
else
108
GOBIN=$(shell go env GOBIN)
119
endif
1210

13-
# CONTAINER_TOOL defines the container tool to be used for building images.
14-
# Be aware that the target commands are only tested with Docker which is
15-
# scaffolded by default. However, you might want to replace it to use other
16-
# tools. (i.e. podman)
1711
CONTAINER_TOOL ?= docker
1812

19-
# Setting SHELL to bash allows bash commands to be executed by recipes.
20-
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
2113
SHELL = /usr/bin/env bash -o pipefail
2214
.SHELLFLAGS = -ec
2315

@@ -48,7 +40,7 @@ vet: ## Run go vet against code.
4840

4941
.PHONY: test
5042
test: manifests generate fmt vet setup-envtest ## Run tests.
51-
KUBEBUILDER_ASSETS="$(shell "$(ENVTEST)" use $(ENVTEST_K8S_VERSION) --bin-dir "$(LOCALBIN)" -p path)" go test ./... -coverprofile cover.out
43+
KUBEBUILDER_ASSETS="$(shell "$(ENVTEST)" use $(ENVTEST_K8S_VERSION) --bin-dir "$(LOCALBIN)" -p path)" go test ./... -v -race -coverprofile cover.out
5244

5345
.PHONY: lint
5446
lint: golangci-lint ## Run golangci-lint linter
@@ -153,6 +145,38 @@ deploy-rbpi: ## Build arm64 image, import on rbpi, deploy (set KUBECONFIG).
153145
$(MAKE) rbpi-verify
154146
@echo "Revert image pin: git checkout -- config/manager/kustomization.yaml"
155147

148+
##@ scale test
149+
150+
LOAD_NS ?= default
151+
LOAD_REDIS_DEPLOY ?= redis
152+
LOAD_STREAM ?= events
153+
LOAD_GROUP ?= workers
154+
LOAD_COUNT ?= 80
155+
LOAD_DEPLOY ?= worker-deployment
156+
LOAD_SCALER ?= redisstreamscaler-sample
157+
158+
LOAD_REDIS_EXEC = $(KUBECTL) exec -n $(LOAD_NS) deploy/$(LOAD_REDIS_DEPLOY) -- redis-cli
159+
160+
.PHONY: scale-load
161+
scale-load: ## XADD messages on LOAD_STREAM (Redis deploy + RedisStreamScaler required).
162+
$(LOAD_REDIS_EXEC) XGROUP CREATE $(LOAD_STREAM) $(LOAD_GROUP) $$ MKSTREAM 2>/dev/null || true
163+
@for i in $$(seq 1 $(LOAD_COUNT)); do \
164+
$(LOAD_REDIS_EXEC) XADD $(LOAD_STREAM) '*' payload $$i >/dev/null; \
165+
done
166+
@echo "Added $(LOAD_COUNT) messages to $(LOAD_STREAM). Run: make scale-observe"
167+
168+
.PHONY: scale-observe
169+
scale-observe: ## Watch deployment replicas and RedisStreamScaler status (Ctrl+C to stop).
170+
watch -n 2 -- \
171+
"$(KUBECTL) get deploy $(LOAD_DEPLOY) -n $(LOAD_NS); \
172+
echo; \
173+
$(KUBECTL) get redisstreamscaler $(LOAD_SCALER) -n $(LOAD_NS) -o custom-columns=NAME:.metadata.name,BACKLOG:.status.currentBacklog,REPLICAS:.status.currentReplicas,READY:.status.conditions[?(@.type==\"Available\")].status"
174+
175+
.PHONY: scale-load-clear
176+
scale-load-clear: ## XTRIM stream empty; scale-down may wait for cooldownPeriod.
177+
$(LOAD_REDIS_EXEC) XTRIM $(LOAD_STREAM) MAXLEN 0
178+
@echo "Cleared $(LOAD_STREAM). Scale-down may wait for cooldownPeriod on the CR."
179+
156180
##@ Dependencies
157181

158182
## Location to install dependencies to
@@ -207,13 +231,11 @@ $(ENVTEST): $(LOCALBIN)
207231

208232
.PHONY: golangci-lint
209233
golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
210-
$(GOLANGCI_LINT): $(LOCALBIN)
234+
$(GOLANGCI_LINT): $(LOCALBIN) .custom-gcl.yml
211235
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))
212-
@test -f .custom-gcl.yml && { \
213-
echo "Building custom golangci-lint with plugins..." && \
214-
$(GOLANGCI_LINT) custom --destination $(LOCALBIN) --name golangci-lint-custom && \
215-
mv -f $(LOCALBIN)/golangci-lint-custom $(GOLANGCI_LINT); \
216-
} || true
236+
@echo "Building custom golangci-lint with plugins..."
237+
$(GOLANGCI_LINT) custom --destination $(LOCALBIN) --name golangci-lint-custom
238+
mv -f $(LOCALBIN)/golangci-lint-custom $(GOLANGCI_LINT)
217239

218240
# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
219241
# $1 - target path with name of binary

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,16 @@ make deploy-rbpi
4949

5050
Details: [docs/deploy.md](docs/deploy.md).
5151

52+
## Scale test
53+
54+
With operator, Redis, worker Deployment, and the sample CR applied:
55+
56+
```bash
57+
make scale-load
58+
make scale-observe
59+
make scale-load-clear
60+
```
61+
5262
## RedisStreamScaler example
5363

5464
```yaml

cmd/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ func main() {
181181
if err := (&controller.RedisStreamScalerReconciler{
182182
Client: mgr.GetClient(),
183183
Scheme: mgr.GetScheme(),
184-
Recorder: mgr.GetEventRecorderFor("redisstreamscaler-controller"),
184+
Recorder: mgr.GetEventRecorderFor("redisstreamscaler-controller"), //nolint:staticcheck,lll
185185
}).SetupWithManager(mgr); err != nil {
186186
setupLog.Error(err, "Failed to create controller", "controller", "redisstreamscaler")
187187
os.Exit(1)

config/samples/kustomization.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
## Example manifests (not installed by make deploy)
22
resources:
33
- autoscaling_v1alpha1_redisstreamscaler.yaml
4-
# - worker-deployment.yaml

config/samples/worker-deployment.yaml

Lines changed: 0 additions & 21 deletions
This file was deleted.

docs/deploy.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,23 @@ git checkout -- config/manager/kustomization.yaml
5454
make undeploy
5555
```
5656

57-
## Demo workload
57+
## Scale test
5858

59-
Use the Redis FQDN when the operator runs in `k8s-redis-workload-scaler-system`:
59+
Apply the sample CR and your worker Deployment (name must match `scaleTargetRef` in the CR, default `worker-deployment`):
6060

6161
```bash
62-
kubectl apply -f config/samples/worker-deployment.yaml
6362
kubectl apply -f config/samples/autoscaling_v1alpha1_redisstreamscaler.yaml
6463
```
6564

66-
Load: `XADD` on stream `events` (group `workers`). Trim: `XTRIM events MAXLEN 0` to scale down after cooldown.
65+
Redis must be reachable at the address in the CR (e.g. `redis.default.svc.cluster.local:6379`). Makefile targets exec into `deploy/redis` in `default`:
66+
67+
```bash
68+
make scale-load # XADD backlog on stream events / group workers
69+
make scale-observe # watch replicas + scaler status (Ctrl+C to stop)
70+
make scale-load-clear # XTRIM stream; scale-down waits for cooldownPeriod
71+
```
72+
73+
Override defaults if needed: `LOAD_NS`, `LOAD_REDIS_DEPLOY`, `LOAD_STREAM`, `LOAD_GROUP`, `LOAD_COUNT`, `LOAD_DEPLOY`, `LOAD_SCALER`.
6774

6875
## GitHub Actions
6976

@@ -75,5 +82,5 @@ Push to `main` builds an arm64 image on GHCR and runs `make deploy` on rbpi via
7582
|---------|-----|
7683
| `Degraded` / `no such host` for `redis` | `redis.<namespace>.svc.cluster.local:6379` |
7784
| Manager `ImagePullBackOff` | Image must be arm64 and present on rbpi (local import or GHCR pull) |
78-
| Worker `ImagePullBackOff` | Use a real image (sample: `busybox:1.36`) |
85+
| Worker `ImagePullBackOff` | Fix your worker Deployment image |
7986
| SSH import fails | Check `Host rbpi` in `~/.ssh/config` or set `RBPI_SSH_KEY` |

internal/controller/redis.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,15 +99,15 @@ func (p *redisClientPool) get(cfg redisConnConfig) *redis.Client {
9999
p.mu.Lock()
100100
defer p.mu.Unlock()
101101

102-
if client, ok := p.clients[key]; ok {
103-
return client
102+
if c, ok := p.clients[key]; ok {
103+
return c
104104
}
105105

106106
opts := &redis.Options{Addr: cfg.addr}
107107
if cfg.password != "" {
108108
opts.Password = cfg.password
109109
}
110-
client := redis.NewClient(opts)
111-
p.clients[key] = client
112-
return client
110+
rdb := redis.NewClient(opts)
111+
p.clients[key] = rdb
112+
return rdb
113113
}

internal/controller/redisstreamscaler_controller_test.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ var _ = Describe("RedisStreamScaler Controller", func() {
6161
Expect(err).NotTo(HaveOccurred())
6262

6363
rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
64-
defer rdb.Close()
64+
defer func() {
65+
Expect(rdb.Close()).To(Succeed())
66+
}()
6567

6668
Expect(rdb.XGroupCreateMkStream(ctx, streamName, groupName, "0").Err()).NotTo(HaveOccurred())
6769

@@ -96,10 +98,10 @@ var _ = Describe("RedisStreamScaler Controller", func() {
9698
Expect(k8sClient.Create(ctx, deploy)).To(Succeed())
9799
}
98100

99-
var scaler *autoscalingv1alpha1.RedisStreamScaler
101+
scaler := &autoscalingv1alpha1.RedisStreamScaler{}
100102

101103
err = k8sClient.Get(ctx, typeNamespacedName, scaler)
102-
if err != nil && errors.IsNotFound(err) {
104+
if errors.IsNotFound(err) {
103105
resource := &autoscalingv1alpha1.RedisStreamScaler{
104106
ObjectMeta: metav1.ObjectMeta{
105107
Name: resourceName,
@@ -116,18 +118,20 @@ var _ = Describe("RedisStreamScaler Controller", func() {
116118
},
117119
}
118120
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
121+
} else {
122+
Expect(err).NotTo(HaveOccurred())
119123
}
120124
})
121125

122126
AfterEach(func() {
123-
var resource *autoscalingv1alpha1.RedisStreamScaler
127+
resource := &autoscalingv1alpha1.RedisStreamScaler{}
124128

125129
err := k8sClient.Get(ctx, typeNamespacedName, resource)
126130
if err == nil {
127131
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
128132
}
129133

130-
var deploy *appsv1.Deployment
134+
deploy := &appsv1.Deployment{}
131135

132136
err = k8sClient.Get(ctx, types.NamespacedName{Name: deploymentName, Namespace: "default"}, deploy)
133137
if err == nil {

0 commit comments

Comments
 (0)