ADR-003: Custom Operator vs OpenSTF/DeviceFarmer¶
Status: Accepted
Context¶
We need a control plane that manages the lifecycle of virtual Android devices, assigns them to test sessions with declarative device templates. OpenSTF and its fork DeviceFarmer exist for this purpose.
Options Considered¶
Option A: OpenSTF / DeviceFarmer¶
- Browser-based remote device management platform
- Originally designed for physical USB-connected devices
- Abandoned by OpenSTF in July 2020 (last release v3.4.1, Android 9 only)
- DeviceFarmer: volunteer-maintained fork, more recent Android support
- Kubernetes operator exists:
tinyzimmer/android-farm-operator(stale, 2020) - Architecture: RethinkDB + Nginx router + Node.js services — complex to operate
- No declarative app configuration concept
- WebRTC streaming via
stf-provider
Option B: Custom Kubernetes Operator (Go, kubebuilder)¶
- Designed precisely for our use case
- CRDs model our exact concepts: DeviceTemplate, DevicePool, TestSession
- Integrates natively with Kubernetes scaling (HPA, KEDA)
- No external state store (uses etcd via CRD status)
- Declarative app configuration is a first-class feature
- Smaller operational surface: only needs Kubernetes
- Dev effort: significant, but the project IS the operator
Decision¶
Custom operator using Go and kubebuilder.
The fundamental mismatch between OpenSTF's USB-device model and our emulator-pod model means we would spend as much effort adapting STF as building a targeted operator. The operator approach also gives us:
- Kubernetes-native status and events (kubectl describe works)
- GitOps-compatible (all config in YAML/CRDs, no web UI config)
- No external database dependency
- Full declarative app config in the DeviceTemplate CRD
Consequences¶
- Device scheduling (claim idle device for session) and stream URL routing are operator responsibilities.
- All configuration lives in YAML/CRDs; no external web UI configuration.
- No external database dependency — state is stored in etcd via CRD status fields.