CRD Reference¶
DroidFarm installs three custom resources in the droidfarm.io/v1alpha1 API group.
DeviceTemplate¶
A declarative profile that describes an Android device configuration.
apiVersion: droidfarm.io/v1alpha1
kind: DeviceTemplate
metadata:
name: myapp-android14
namespace: droidfarm-system
spec:
androidVersion: "14" # "13" | "14" | "15"
avdProfile: pixel_6 # Any valid AVD hardware profile
resources:
cpu: "2"
memory: "4Gi"
appConfig:
- packageName: com.example.myapp # Must match [\w.]+
apkSource:
url: https://artifacts.example.com/app.apk # Must be https://
managedConfig:
- key: api_endpoint
value: https://staging.api.example.com
permissions:
- android.permission.CAMERA
- android.permission.ACCESS_FINE_LOCATION
autoLaunch: true
clearDataBetweenSessions: true
- packageName: com.companion.app # Additional apps
apkSource:
url: https://artifacts.example.com/companion.apk
systemConfig:
locale: en_US
timezone: UTC
testConfig:
resetStrategy: fastReset # fastReset | fullReset
Spec fields¶
| Field | Type | Required | Description |
|---|---|---|---|
androidVersion | string | yes | Android API level image to use ("13", "14", "15") |
avdProfile | string | yes | AVD hardware profile (e.g. pixel_6, pixel_4a) |
emulationBackend | string | no | cuttlefish-qemu (default, no KVM, WebRTC built-in) or cuttlefish (KVM, faster boot) |
resources.cpu | string | yes | CPU request/limit for emulator pod |
resources.memory | string | yes | Memory request/limit for emulator pod |
appConfig | []AppEntry | no | Apps to install; first entry is the primary AUT |
appConfig[].packageName | string | yes | Android package name (validated with regex [\w.]+) |
appConfig[].apkSource.url | string | yes | HTTPS URL to APK (must start with https://) |
appConfig[].managedConfig | []KV | no | Android Enterprise managed app config key/value pairs |
appConfig[].permissions | []string | no | Permissions to grant via ADB |
appConfig[].autoLaunch | bool | no | Launch app on device ready |
appConfig[].clearDataBetweenSessions | bool | no | adb shell pm clear before each session |
systemConfig.locale | string | no | Device locale (default: en_US) |
systemConfig.timezone | string | no | Device timezone (default: UTC) |
testConfig.resetStrategy | string | no | fastReset (clear data) or fullReset (wipe and reinstall) |
DevicePool¶
A fleet of emulators backed by a DeviceTemplate.
apiVersion: droidfarm.io/v1alpha1
kind: DevicePool
metadata:
name: staging-pool
namespace: droidfarm-system
spec:
templateRef:
name: myapp-android14
replicas:
min: 1
max: 8
sessionPolicy:
timeout: 30m
recycleAfterSessions: 10
streaming:
enabled: true
domain: local.geekxflood.io
# The defaults below target the cluster's shared Cilium Gateway.
# Override only when running a dedicated streaming Gateway.
# gatewayName: cilium-gateway
# gatewayNamespace: kube-system
# gatewaySectionName: https
appium:
enabled: true
Spec fields¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
templateRef.name | string | yes | — | Name of DeviceTemplate in the same namespace |
replicas.min | int | yes | — | Minimum devices always running |
replicas.max | int | yes | — | Maximum devices (KEDA scales up to this) |
sessionPolicy.timeout | duration | no | 30m | Maximum session duration |
sessionPolicy.recycleAfterSessions | int | no | — | Replace pod after N sessions |
nodeSelector | map | no | — | Optional. Add kvm: "true" only when using emulationBackend: cuttlefish. |
streaming.enabled | bool | no | true | Create per-device HTTPRoute objects |
streaming.domain | string | no | — | Base domain; per-device hostnames are <pool>-<ordinal>.<domain> |
streaming.gatewayName | string | no | cilium-gateway | Name of the Gateway the HTTPRoutes attach to |
streaming.gatewayNamespace | string | no | kube-system | Namespace of the Gateway |
streaming.gatewaySectionName | string | no | https | Listener section on the Gateway |
appium.enabled | bool | no | — | Start Appium server sidecar |
The streaming defaults target the cluster's shared Cilium Gateway (kube-system/cilium-gateway, listener section https) — the Cilium 1.18+ cluster-wide Gateway pattern. The Gateway's https listener must carry a wildcard certificate that covers *.<streaming.domain> and its LoadBalancer address must resolve for that wildcard. Override gatewayName / gatewayNamespace / gatewaySectionName only when attaching the pool to a dedicated streaming Gateway — see streaming.gateway chart values.
Status fields¶
| Field | Description |
|---|---|
status.readyReplicas | Devices in Idle state |
status.devices | Per-device state map (Booting, Idle, Busy) |
conditions | Standard Kubernetes conditions |
TestSession¶
A single E2E test run that claims an Idle device from a pool.
apiVersion: droidfarm.io/v1alpha1
kind: TestSession
metadata:
generateName: myapp-e2e-
namespace: droidfarm-system
spec:
poolRef:
name: staging-pool
timeout: 45m
appium:
configMapRef:
name: myapp-appium-caps
artifacts:
recording: true
s3:
bucket: droidfarm-results
prefix: runs/
Spec fields¶
| Field | Type | Required | Description |
|---|---|---|---|
poolRef.name | string | yes | DevicePool to claim a device from |
timeout | duration | no | Hard timeout; session is terminated after this (default: 30m) |
appium.configMapRef.name | string | no | ConfigMap holding Appium desired capabilities |
artifacts.recording | bool | no | Enable screen recording |
artifacts.s3.bucket | string | no | S3 bucket for artifacts |
artifacts.s3.prefix | string | no | S3 key prefix |
Status fields¶
| Field | Description |
|---|---|
status.phase | Pending, Claiming, Preparing, Running, Collecting, Succeeded, Failed, TimedOut |
status.result | Pass, Fail, Error, Skipped — set by the external runner or operator |
status.appiumEndpoint | WebDriver URL: http://<pod-name>.droidfarm-system.svc:4723 |
status.streamURL | Browser stream URL: https://<pool>-<ordinal>.<streaming.domain> — served by the cluster's shared Cilium Gateway (kube-system/cilium-gateway, https listener) via the per-device HTTPRoute |
status.deviceRef | Pod name of the claimed device |
status.startTime | When the session entered Running |
status.endTime | When the session reached a terminal phase |
status.duration | Human-readable session duration (set after terminal phase) |
status.artifactURL | URL to uploaded recordings/results (set after Collecting) |
status.failureMessage | Human-readable failure reason (set on Failed or TimedOut) |