├── LICENSE ├── README.md ├── grafana └── openstf-dashboard.json ├── openstf ├── Chart.yaml ├── templates │ ├── _helpers.tpl │ ├── configmap.yaml │ ├── daemonset.yaml │ ├── deployment.yaml │ ├── emulators.yaml │ ├── ingress.yaml │ ├── pre-install-job.yaml │ └── svc.yaml └── values.yaml └── scripts ├── .gitignore └── clean-offline-x86.js /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # android-farm 2 | Android devices farm with USB and emulated devices support 3 | 4 | ## TL;DR; 5 | 6 | ```console 7 | $ helm install openstf 8 | ``` 9 | 10 | ## Introduction 11 | 12 | This project contains a chart that bootstraps an OpenSTF deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. 13 | 14 | # Prerequisites 15 | - Kubernetes cluster with KVM-capable nodes for emulators 16 | - [helm](https://helm.sh) 17 | - [rethinkdb installation](https://github.com/kubernetes/charts/tree/master/stable/rethinkdb) 18 | - [adb-butler](https://github.com/agoda-com/adb-butler) image 19 | - [docker-emulator-android](https://github.com/agoda-com/docker-emulator-android) images 20 | 21 | ## Installing the Chart 22 | 23 | To install the chart with the release name `my-release`: 24 | 25 | ```console 26 | $ helm install --name my-release openstf 27 | ``` 28 | 29 | The command deploys OpenSTF on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation. 30 | 31 | You need to set `emulators.imagePrefix` and `adb.image.repository` values to the images of [docker-emulator-android](https://github.com/agoda-com/docker-emulator-android) and [adb-butler](https://github.com/agoda-com/adb-butler) you've built in order for the chart to work properly. This can be done either in `values.yaml` or via the command-line. The full installation will look something like: 32 | 33 | ```console 34 | $ helm install --name openstf -f openstf/values.yaml \ 35 | --set emulators.imagePrefix=docker-registry/agoda/docker-emulator-android- \ 36 | --set emulators.imageVersion=1.0.0 \ 37 | --set adb.image.repository=docker-registry/agoda/adb-butler \ 38 | --set pullSecret=docker-registry-secret \ 39 | --set ingress.enabled=true \ 40 | --set ingress.hostname=openstf.svc.cluster.local \ 41 | --set ingress.ssl.enabled=true \ 42 | --set ingress.ssl.secret=ssl-secret \ 43 | --set rethinkdb.password=strongpassword \ 44 | openstf 45 | ``` 46 | 47 | > **Tip**: List all releases using `helm list` 48 | 49 | ## Uninstalling the Chart 50 | 51 | To uninstall/delete the `my-release` deployment: 52 | 53 | ```console 54 | $ helm delete my-release 55 | ``` 56 | 57 | The command removes all the Kubernetes components associated with the chart and deletes the release. 58 | 59 | # Nodes configuration 60 | You need to label and taint the usb provider nodes and kvm nodes as following 61 | 62 | USB providers: 63 | ``` 64 | $ kubectl label nodes node-x.cluster.local openstf.io/provider=true 65 | $ kubectl taint nodes node-x.cluster.local openstf.io/provider=true:NoSchedule 66 | ``` 67 | 68 | Emulator nodes: 69 | ``` 70 | $ kubectl label nodes node-x.cluster.local openstf.io/emulator=kvm 71 | ``` 72 | 73 | # Configuration 74 | 75 | The following tables lists the configurable parameters of the openstf chart and their default values. 76 | 77 | Parameter | Description | Default 78 | --- | --- | --- 79 | `stf.image.repository` | OpenSTF container image repository | `openstf/stf` 80 | `stf.image.tag` | OpenSTF container image tag | `v3.2.0` 81 | `stf.image.pullPolicy` | OpenSTF container image pull policy | `IfNotPresent` 82 | `stf.api.replicas` | Desired number of API pods | `3` 83 | `stf.app.replicas` | Desired number of app pods | `1` 84 | `stf.auth.replicas` | Desired number of auth pods | `1` 85 | `stf.processor.replicas` | Desired number of processor pods | `5` 86 | `stf.reaper.replicas` | Desired number of reaper pods | `1` 87 | `stf.apkStorage.replicas` | Desired number of apk-storage pods | `1` 88 | `stf.imgStorage.replicas` | Desired number of image-storage pods | `1` 89 | `stf.storage.replicas` | Desired number of storage pods | `1` 90 | `stf.triproxyDev.replicas` | Desired number of triproxy-dev pods | `1` 91 | `stf.triproxyApp.replicas` | Desired number of triproxy-app pods | `1` 92 | `stf.websocket.replicas` | Desired number of websocket pods | `1` 93 | `nginx.image.repository` | nginx container image repository | `nginx` 94 | `nginx.image.tag` | nginx container image repository tag | `1.13.8-alpine` 95 | `nginx.image.pullPolicy` | nginx container image pullPolicy | `IfNotPresent` 96 | `nginx.replicas` | Desired number of nginx pods | `1` 97 | `adb.image.repository` | adb-butler container image repository | `agoda/adb-butler` 98 | `adb.image.tag` | adb-butler container image repository tag | `latest` 99 | `adb.image.pullPolicy` | adb-butler container image pullPolicy | `Always` 100 | `db.url` | URL of RethinkDB | `rethinkdb-rethinkdb-proxy.openstf` 101 | `db.port` | TCP port of RethinkDB | `28015` 102 | `db.password` | RethinkDB password | `rethinkdb` 103 | `dns.resolver` | Address of DNS server | `kube-dns.kube-system` 104 | `pullSecret` | Pull secret for pulling all the images | `""` 105 | `ingress.enabled` | Enable ingress controller resource | `false` 106 | `ingress.annotations` | Custom annotations on ingress resource | `nginx.org/websocket-services: nginx` 107 | `ingress.hostname` | Hostname to your OpenSTF installation | `openstf.local` 108 | `ingress.ssl.enabled` | Utilize TLS backend in ingress | `false` 109 | `ingress.ssl.secret` | TLS Secret (certificates) | `openstf.local-tls-secret` 110 | `emulators.imagePrefix` | Prefix of docker android container image | `agoda/docker-emulator-android-` 111 | `emulators.imageVersion` | Docker android container image tag | `25` 112 | `emulators.pullPolicy` | Docker android container image pull policy | `IfNotPresent` 113 | `emulator.types` | Types of docker android emulators to spawn | `see below for more info` 114 | `telegraf.image.repository` | telegraf container image repository | `telegraf` 115 | `telegraf.image.tag` | telegraf container image repository tag | `1.5-alpine` 116 | `telegraf.image.pullPolicy` | telegraf container image pullPolicy | `IfNotPresent` 117 | `telegraf.config.outputs.prometheus.enabled` | provide prometheus metrics on provider pods | `true` 118 | 119 | > **Tip**: You can use the default [values.yaml](openstf/values.yaml) 120 | 121 | ## Pod resources 122 | All the pods have sane default resources set. To customize check the [values.yaml](openstf/values.yaml) 123 | 124 | ## Emulators 125 | In order to create emulators you need to provide configuration inside `emulators.types`. For example you want the to name the batch of devices `tablet7i`, add `ci/uiTest/tablet/7inch` note to all these devices in OpenSTF and change the `config.ini` variable for emulator. You also want 3 instances of such config with API version 26 and 3 instances with API version 19. 126 | 127 | ```YAML 128 | - name: tablet7i 129 | note: ci/uiTest/tablet/7inch 130 | args: "skin.name=600x1024;hw.lcd.density=160;hw.lcd.height=600;hw.lcd.width=1024;hw.device.name=7in WSVGA (Tablet);avd.ini.displayname=7 WSVGA (Tablet) API 23;" 131 | instances: 132 | - version: 26 133 | count: 3 134 | - version: 19 135 | count: 3 136 | ``` 137 | 138 | By default you'll have phones with API versions 17 to 26 and 7' + 10' tablets with API version 25. 139 | 140 | # License 141 | 142 | android-farm is open source and available under the [Apache License, Version 2.0](LICENSE). 143 | 144 | OpenSTF is open source and available under the [Apache License, Version 2.0](https://github.com/openstf/stf/blob/master/LICENSE) 145 | 146 | Android SDK components are available under the [Android Software Development Kit License](https://developer.android.com/studio/terms.html) 147 | 148 | # Related projects 149 | 150 | The emulators are spawned using the 151 | [docker-emulator-android](https://github.com/agoda-com/docker-emulator-android) container image 152 | 153 | Default adb side container image with self-healing and metrics is [adb-butler](https://github.com/agoda-com/adb-butler) 154 | 155 | Connecting to OpenSTF devices with filtering support and reconnect logic is done using [stf-client](https://github.com/Malinskiy/stf-client) 156 | 157 | [fork](https://github.com/agoda-com/fork) is an instrumentation runner with support for reconnecting to adb devices on-the-go and more 158 | 159 | [example of ci agent container](https://github.com/Malinskiy/docker-android) 160 | -------------------------------------------------------------------------------- /grafana/openstf-dashboard.json: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": { 3 | "list": [ 4 | { 5 | "builtIn": 1, 6 | "datasource": "-- Grafana --", 7 | "enable": true, 8 | "hide": true, 9 | "iconColor": "rgba(0, 211, 255, 1)", 10 | "name": "Annotations & Alerts", 11 | "type": "dashboard" 12 | } 13 | ] 14 | }, 15 | "editable": true, 16 | "gnetId": null, 17 | "graphTooltip": 0, 18 | "hideControls": false, 19 | "id": 4, 20 | "links": [], 21 | "refresh": "30s", 22 | "rows": [ 23 | { 24 | "collapse": false, 25 | "height": "80", 26 | "panels": [ 27 | { 28 | "cacheTimeout": null, 29 | "colorBackground": false, 30 | "colorValue": true, 31 | "colors": [ 32 | "#299c46", 33 | "rgba(237, 129, 40, 0.89)", 34 | "#d44a3a" 35 | ], 36 | "datasource": "$datasource", 37 | "format": "none", 38 | "gauge": { 39 | "maxValue": 100, 40 | "minValue": 0, 41 | "show": false, 42 | "thresholdLabels": false, 43 | "thresholdMarkers": true 44 | }, 45 | "id": 12, 46 | "interval": null, 47 | "links": [], 48 | "mappingType": 1, 49 | "mappingTypes": [ 50 | { 51 | "name": "value to text", 52 | "value": 1 53 | }, 54 | { 55 | "name": "range to text", 56 | "value": 2 57 | } 58 | ], 59 | "maxDataPoints": 100, 60 | "nullPointMode": "connected", 61 | "nullText": null, 62 | "postfix": "", 63 | "postfixFontSize": "50%", 64 | "prefix": "", 65 | "prefixFontSize": "50%", 66 | "rangeMaps": [ 67 | { 68 | "from": "null", 69 | "text": "N/A", 70 | "to": "null" 71 | } 72 | ], 73 | "span": 4, 74 | "sparkline": { 75 | "fillColor": "rgba(31, 118, 189, 0.18)", 76 | "full": true, 77 | "lineColor": "rgb(31, 120, 193)", 78 | "show": true 79 | }, 80 | "tableColumn": "", 81 | "targets": [ 82 | { 83 | "expr": "max(android_battery_temperature)", 84 | "format": "time_series", 85 | "intervalFactor": 2, 86 | "legendFormat": "", 87 | "refId": "A" 88 | } 89 | ], 90 | "thresholds": "40,50", 91 | "title": "Max battery temp", 92 | "type": "singlestat", 93 | "valueFontSize": "200%", 94 | "valueMaps": [ 95 | { 96 | "op": "=", 97 | "text": "N/A", 98 | "value": "null" 99 | } 100 | ], 101 | "valueName": "current" 102 | }, 103 | { 104 | "cacheTimeout": null, 105 | "colorBackground": false, 106 | "colorValue": true, 107 | "colors": [ 108 | "#299c46", 109 | "rgba(237, 129, 40, 0.89)", 110 | "#d44a3a" 111 | ], 112 | "datasource": "$datasource", 113 | "format": "none", 114 | "gauge": { 115 | "maxValue": 100, 116 | "minValue": 0, 117 | "show": false, 118 | "thresholdLabels": false, 119 | "thresholdMarkers": true 120 | }, 121 | "id": 13, 122 | "interval": null, 123 | "links": [], 124 | "mappingType": 1, 125 | "mappingTypes": [ 126 | { 127 | "name": "value to text", 128 | "value": 1 129 | }, 130 | { 131 | "name": "range to text", 132 | "value": 2 133 | } 134 | ], 135 | "maxDataPoints": 100, 136 | "nullPointMode": "connected", 137 | "nullText": null, 138 | "postfix": "", 139 | "postfixFontSize": "50%", 140 | "prefix": "", 141 | "prefixFontSize": "50%", 142 | "rangeMaps": [ 143 | { 144 | "from": "null", 145 | "text": "N/A", 146 | "to": "null" 147 | } 148 | ], 149 | "span": 4, 150 | "sparkline": { 151 | "fillColor": "rgba(31, 118, 189, 0.18)", 152 | "full": true, 153 | "lineColor": "rgb(31, 120, 193)", 154 | "show": true 155 | }, 156 | "tableColumn": "", 157 | "targets": [ 158 | { 159 | "expr": "min(android_battery_temperature)", 160 | "format": "time_series", 161 | "intervalFactor": 2, 162 | "legendFormat": "", 163 | "refId": "A" 164 | } 165 | ], 166 | "thresholds": "40,50", 167 | "title": "Min battery temp", 168 | "type": "singlestat", 169 | "valueFontSize": "200%", 170 | "valueMaps": [ 171 | { 172 | "op": "=", 173 | "text": "N/A", 174 | "value": "null" 175 | } 176 | ], 177 | "valueName": "current" 178 | }, 179 | { 180 | "cacheTimeout": null, 181 | "colorBackground": false, 182 | "colorValue": false, 183 | "colors": [ 184 | "#299c46", 185 | "rgba(237, 129, 40, 0.89)", 186 | "#d44a3a" 187 | ], 188 | "datasource": "$datasource", 189 | "format": "none", 190 | "gauge": { 191 | "maxValue": 100, 192 | "minValue": 0, 193 | "show": false, 194 | "thresholdLabels": false, 195 | "thresholdMarkers": true 196 | }, 197 | "id": 14, 198 | "interval": null, 199 | "links": [], 200 | "mappingType": 1, 201 | "mappingTypes": [ 202 | { 203 | "name": "value to text", 204 | "value": 1 205 | }, 206 | { 207 | "name": "range to text", 208 | "value": 2 209 | } 210 | ], 211 | "maxDataPoints": 100, 212 | "nullPointMode": "connected", 213 | "nullText": null, 214 | "postfix": "", 215 | "postfixFontSize": "50%", 216 | "prefix": "", 217 | "prefixFontSize": "50%", 218 | "rangeMaps": [ 219 | { 220 | "from": "null", 221 | "text": "N/A", 222 | "to": "null" 223 | } 224 | ], 225 | "span": 4, 226 | "sparkline": { 227 | "fillColor": "rgba(31, 118, 189, 0.18)", 228 | "full": true, 229 | "lineColor": "rgb(31, 120, 193)", 230 | "show": true 231 | }, 232 | "tableColumn": "", 233 | "targets": [ 234 | { 235 | "expr": "count(openstf_status{present=\"true\"})", 236 | "format": "time_series", 237 | "intervalFactor": 2, 238 | "refId": "A" 239 | } 240 | ], 241 | "thresholds": "", 242 | "title": "Present devices count", 243 | "type": "singlestat", 244 | "valueFontSize": "200%", 245 | "valueMaps": [ 246 | { 247 | "op": "=", 248 | "text": "N/A", 249 | "value": "null" 250 | } 251 | ], 252 | "valueName": "current" 253 | } 254 | ], 255 | "repeat": null, 256 | "repeatIteration": null, 257 | "repeatRowId": null, 258 | "showTitle": false, 259 | "title": "Dashboard Row", 260 | "titleSize": "h6" 261 | }, 262 | { 263 | "collapse": false, 264 | "height": "300", 265 | "panels": [ 266 | { 267 | "aliasColors": {}, 268 | "bars": false, 269 | "dashLength": 10, 270 | "dashes": false, 271 | "datasource": "$datasource", 272 | "fill": 1, 273 | "id": 2, 274 | "legend": { 275 | "avg": false, 276 | "current": false, 277 | "max": false, 278 | "min": false, 279 | "show": true, 280 | "total": false, 281 | "values": false 282 | }, 283 | "lines": true, 284 | "linewidth": 1, 285 | "links": [], 286 | "nullPointMode": "null", 287 | "percentage": false, 288 | "pointradius": 5, 289 | "points": false, 290 | "renderer": "flot", 291 | "seriesOverrides": [], 292 | "spaceLength": 10, 293 | "span": 6, 294 | "stack": true, 295 | "steppedLine": false, 296 | "targets": [ 297 | { 298 | "expr": "count(android) by (host)", 299 | "format": "time_series", 300 | "intervalFactor": 2, 301 | "legendFormat": "{{host}}", 302 | "refId": "A" 303 | } 304 | ], 305 | "thresholds": [], 306 | "timeFrom": null, 307 | "timeShift": null, 308 | "title": "Providers", 309 | "tooltip": { 310 | "shared": true, 311 | "sort": 0, 312 | "value_type": "individual" 313 | }, 314 | "type": "graph", 315 | "xaxis": { 316 | "buckets": null, 317 | "mode": "time", 318 | "name": null, 319 | "show": true, 320 | "values": [] 321 | }, 322 | "yaxes": [ 323 | { 324 | "decimals": null, 325 | "format": "short", 326 | "label": null, 327 | "logBase": 1, 328 | "max": null, 329 | "min": "0", 330 | "show": true 331 | }, 332 | { 333 | "format": "short", 334 | "label": null, 335 | "logBase": 1, 336 | "max": null, 337 | "min": null, 338 | "show": true 339 | } 340 | ] 341 | }, 342 | { 343 | "aliasColors": {}, 344 | "bars": false, 345 | "dashLength": 10, 346 | "dashes": false, 347 | "datasource": "$datasource", 348 | "fill": 0, 349 | "id": 10, 350 | "legend": { 351 | "avg": false, 352 | "current": false, 353 | "max": false, 354 | "min": false, 355 | "show": true, 356 | "total": false, 357 | "values": false 358 | }, 359 | "lines": true, 360 | "linewidth": 1, 361 | "links": [], 362 | "nullPointMode": "null", 363 | "percentage": false, 364 | "pointradius": 5, 365 | "points": false, 366 | "renderer": "flot", 367 | "seriesOverrides": [], 368 | "spaceLength": 10, 369 | "span": 6, 370 | "stack": false, 371 | "steppedLine": false, 372 | "targets": [ 373 | { 374 | "expr": "avg(android_battery_temperature) by (host)", 375 | "format": "time_series", 376 | "intervalFactor": 2, 377 | "legendFormat": "{{host}}", 378 | "refId": "A" 379 | } 380 | ], 381 | "thresholds": [], 382 | "timeFrom": null, 383 | "timeShift": null, 384 | "title": "Battery temperature [node average]", 385 | "tooltip": { 386 | "shared": true, 387 | "sort": 0, 388 | "value_type": "individual" 389 | }, 390 | "type": "graph", 391 | "xaxis": { 392 | "buckets": null, 393 | "mode": "time", 394 | "name": null, 395 | "show": true, 396 | "values": [] 397 | }, 398 | "yaxes": [ 399 | { 400 | "decimals": null, 401 | "format": "celsius", 402 | "label": null, 403 | "logBase": 1, 404 | "max": null, 405 | "min": "0", 406 | "show": true 407 | }, 408 | { 409 | "format": "short", 410 | "label": null, 411 | "logBase": 1, 412 | "max": null, 413 | "min": null, 414 | "show": true 415 | } 416 | ] 417 | } 418 | ], 419 | "repeat": null, 420 | "repeatIteration": null, 421 | "repeatRowId": null, 422 | "showTitle": false, 423 | "title": "Dashboard Row", 424 | "titleSize": "h6" 425 | }, 426 | { 427 | "collapse": false, 428 | "height": "300", 429 | "panels": [ 430 | { 431 | "aliasColors": {}, 432 | "bars": false, 433 | "dashLength": 10, 434 | "dashes": false, 435 | "datasource": "$datasource", 436 | "fill": 1, 437 | "id": 3, 438 | "legend": { 439 | "alignAsTable": true, 440 | "avg": false, 441 | "current": false, 442 | "max": false, 443 | "min": false, 444 | "rightSide": true, 445 | "show": true, 446 | "total": false, 447 | "values": false 448 | }, 449 | "lines": true, 450 | "linewidth": 1, 451 | "links": [], 452 | "nullPointMode": "null", 453 | "percentage": false, 454 | "pointradius": 5, 455 | "points": false, 456 | "renderer": "flot", 457 | "seriesOverrides": [], 458 | "spaceLength": 10, 459 | "span": 4, 460 | "stack": false, 461 | "steppedLine": false, 462 | "targets": [ 463 | { 464 | "expr": "count(android{status!=\"online\"}) by (serial,host)", 465 | "format": "time_series", 466 | "intervalFactor": 2, 467 | "legendFormat": "{{serial}}-{{host}}", 468 | "refId": "A" 469 | } 470 | ], 471 | "thresholds": [], 472 | "timeFrom": null, 473 | "timeShift": null, 474 | "title": "Devices with flaky adb connection", 475 | "tooltip": { 476 | "shared": true, 477 | "sort": 0, 478 | "value_type": "individual" 479 | }, 480 | "type": "graph", 481 | "xaxis": { 482 | "buckets": null, 483 | "mode": "time", 484 | "name": null, 485 | "show": true, 486 | "values": [] 487 | }, 488 | "yaxes": [ 489 | { 490 | "decimals": 0, 491 | "format": "short", 492 | "label": null, 493 | "logBase": 1, 494 | "max": "1", 495 | "min": "0", 496 | "show": true 497 | }, 498 | { 499 | "format": "short", 500 | "label": null, 501 | "logBase": 1, 502 | "max": null, 503 | "min": null, 504 | "show": true 505 | } 506 | ] 507 | }, 508 | { 509 | "aliasColors": {}, 510 | "bars": false, 511 | "dashLength": 10, 512 | "dashes": false, 513 | "datasource": "$datasource", 514 | "fill": 0, 515 | "id": 9, 516 | "legend": { 517 | "alignAsTable": false, 518 | "avg": false, 519 | "current": false, 520 | "max": false, 521 | "min": false, 522 | "rightSide": false, 523 | "show": false, 524 | "total": false, 525 | "values": false 526 | }, 527 | "lines": true, 528 | "linewidth": 2, 529 | "links": [], 530 | "nullPointMode": "null", 531 | "percentage": false, 532 | "pointradius": 5, 533 | "points": false, 534 | "renderer": "flot", 535 | "seriesOverrides": [], 536 | "spaceLength": 10, 537 | "span": 4, 538 | "stack": false, 539 | "steppedLine": false, 540 | "targets": [ 541 | { 542 | "expr": "avg(android_battery_level) by (serial)", 543 | "format": "time_series", 544 | "intervalFactor": 2, 545 | "legendFormat": "{{serial}}", 546 | "refId": "A" 547 | } 548 | ], 549 | "thresholds": [], 550 | "timeFrom": null, 551 | "timeShift": null, 552 | "title": "Battery level", 553 | "tooltip": { 554 | "shared": false, 555 | "sort": 0, 556 | "value_type": "individual" 557 | }, 558 | "type": "graph", 559 | "xaxis": { 560 | "buckets": null, 561 | "mode": "time", 562 | "name": null, 563 | "show": true, 564 | "values": [] 565 | }, 566 | "yaxes": [ 567 | { 568 | "format": "percent", 569 | "label": null, 570 | "logBase": 1, 571 | "max": "100", 572 | "min": "0", 573 | "show": true 574 | }, 575 | { 576 | "format": "short", 577 | "label": null, 578 | "logBase": 1, 579 | "max": null, 580 | "min": null, 581 | "show": true 582 | } 583 | ] 584 | }, 585 | { 586 | "aliasColors": {}, 587 | "bars": true, 588 | "dashLength": 10, 589 | "dashes": false, 590 | "datasource": "$datasource", 591 | "fill": 1, 592 | "id": 11, 593 | "legend": { 594 | "alignAsTable": true, 595 | "avg": false, 596 | "current": false, 597 | "max": false, 598 | "min": false, 599 | "rightSide": true, 600 | "show": false, 601 | "total": false, 602 | "values": false 603 | }, 604 | "lines": false, 605 | "linewidth": 1, 606 | "links": [], 607 | "nullPointMode": "null", 608 | "percentage": false, 609 | "pointradius": 5, 610 | "points": false, 611 | "renderer": "flot", 612 | "seriesOverrides": [], 613 | "spaceLength": 10, 614 | "span": 4, 615 | "stack": false, 616 | "steppedLine": false, 617 | "targets": [ 618 | { 619 | "expr": "sum(openstf_connect_fail_count) by (serial)", 620 | "format": "time_series", 621 | "intervalFactor": 2, 622 | "legendFormat": "{{serial}}", 623 | "refId": "A" 624 | } 625 | ], 626 | "thresholds": [], 627 | "timeFrom": null, 628 | "timeShift": null, 629 | "title": "stf-client connection failures", 630 | "tooltip": { 631 | "shared": false, 632 | "sort": 0, 633 | "value_type": "individual" 634 | }, 635 | "type": "graph", 636 | "xaxis": { 637 | "buckets": 2, 638 | "mode": "series", 639 | "name": null, 640 | "show": true, 641 | "values": [ 642 | "total" 643 | ] 644 | }, 645 | "yaxes": [ 646 | { 647 | "decimals": 0, 648 | "format": "short", 649 | "label": null, 650 | "logBase": 1, 651 | "max": null, 652 | "min": null, 653 | "show": true 654 | }, 655 | { 656 | "format": "short", 657 | "label": null, 658 | "logBase": 1, 659 | "max": null, 660 | "min": null, 661 | "show": true 662 | } 663 | ] 664 | } 665 | ], 666 | "repeat": null, 667 | "repeatIteration": null, 668 | "repeatRowId": null, 669 | "showTitle": false, 670 | "title": "Dashboard Row", 671 | "titleSize": "h6" 672 | }, 673 | { 674 | "collapse": false, 675 | "height": 250, 676 | "panels": [ 677 | { 678 | "aliasColors": {}, 679 | "bars": true, 680 | "dashLength": 10, 681 | "dashes": false, 682 | "datasource": "$datasource", 683 | "fill": 1, 684 | "id": 5, 685 | "legend": { 686 | "avg": false, 687 | "current": false, 688 | "max": false, 689 | "min": false, 690 | "show": false, 691 | "total": false, 692 | "values": false 693 | }, 694 | "lines": false, 695 | "linewidth": 1, 696 | "links": [], 697 | "nullPointMode": "null", 698 | "percentage": false, 699 | "pointradius": 5, 700 | "points": false, 701 | "renderer": "flot", 702 | "seriesOverrides": [], 703 | "spaceLength": 10, 704 | "span": 3, 705 | "stack": false, 706 | "steppedLine": false, 707 | "targets": [ 708 | { 709 | "expr": "count(openstf_status) by (battery_health)", 710 | "format": "time_series", 711 | "intervalFactor": 2, 712 | "legendFormat": "{{battery_health}}", 713 | "refId": "A" 714 | } 715 | ], 716 | "thresholds": [], 717 | "timeFrom": null, 718 | "timeShift": null, 719 | "title": "Battery health", 720 | "tooltip": { 721 | "shared": false, 722 | "sort": 0, 723 | "value_type": "individual" 724 | }, 725 | "type": "graph", 726 | "xaxis": { 727 | "buckets": null, 728 | "mode": "series", 729 | "name": null, 730 | "show": true, 731 | "values": [ 732 | "current" 733 | ] 734 | }, 735 | "yaxes": [ 736 | { 737 | "format": "short", 738 | "label": null, 739 | "logBase": 1, 740 | "max": null, 741 | "min": null, 742 | "show": true 743 | }, 744 | { 745 | "format": "short", 746 | "label": null, 747 | "logBase": 1, 748 | "max": null, 749 | "min": null, 750 | "show": true 751 | } 752 | ] 753 | }, 754 | { 755 | "aliasColors": {}, 756 | "bars": true, 757 | "dashLength": 10, 758 | "dashes": false, 759 | "datasource": "$datasource", 760 | "fill": 1, 761 | "id": 7, 762 | "legend": { 763 | "avg": false, 764 | "current": false, 765 | "max": false, 766 | "min": false, 767 | "show": false, 768 | "total": false, 769 | "values": false 770 | }, 771 | "lines": false, 772 | "linewidth": 1, 773 | "links": [], 774 | "nullPointMode": "null", 775 | "percentage": false, 776 | "pointradius": 5, 777 | "points": false, 778 | "renderer": "flot", 779 | "seriesOverrides": [], 780 | "spaceLength": 10, 781 | "span": 3, 782 | "stack": false, 783 | "steppedLine": false, 784 | "targets": [ 785 | { 786 | "expr": "count(openstf_status) by (version)", 787 | "format": "time_series", 788 | "intervalFactor": 2, 789 | "legendFormat": "{{version}}", 790 | "refId": "A" 791 | } 792 | ], 793 | "thresholds": [], 794 | "timeFrom": null, 795 | "timeShift": null, 796 | "title": "Android versions", 797 | "tooltip": { 798 | "shared": false, 799 | "sort": 0, 800 | "value_type": "individual" 801 | }, 802 | "type": "graph", 803 | "xaxis": { 804 | "buckets": null, 805 | "mode": "series", 806 | "name": null, 807 | "show": true, 808 | "values": [ 809 | "current" 810 | ] 811 | }, 812 | "yaxes": [ 813 | { 814 | "format": "short", 815 | "label": null, 816 | "logBase": 1, 817 | "max": null, 818 | "min": null, 819 | "show": true 820 | }, 821 | { 822 | "format": "short", 823 | "label": null, 824 | "logBase": 1, 825 | "max": null, 826 | "min": null, 827 | "show": true 828 | } 829 | ] 830 | }, 831 | { 832 | "aliasColors": {}, 833 | "bars": false, 834 | "dashLength": 10, 835 | "dashes": false, 836 | "datasource": "$datasource", 837 | "fill": 1, 838 | "id": 4, 839 | "legend": { 840 | "alignAsTable": false, 841 | "avg": false, 842 | "current": false, 843 | "max": false, 844 | "min": false, 845 | "rightSide": false, 846 | "show": true, 847 | "total": false, 848 | "values": false 849 | }, 850 | "lines": true, 851 | "linewidth": 1, 852 | "links": [], 853 | "nullPointMode": "null", 854 | "percentage": false, 855 | "pointradius": 5, 856 | "points": false, 857 | "renderer": "flot", 858 | "seriesOverrides": [], 859 | "spaceLength": 10, 860 | "span": 3, 861 | "stack": false, 862 | "steppedLine": false, 863 | "targets": [ 864 | { 865 | "expr": "count(openstf_status{abi!='x86',present='true'})", 866 | "format": "time_series", 867 | "hide": false, 868 | "intervalFactor": 2, 869 | "legendFormat": "Not x86 in OpenSTF API", 870 | "refId": "A" 871 | }, 872 | { 873 | "expr": "count(android)", 874 | "format": "time_series", 875 | "intervalFactor": 2, 876 | "legendFormat": "Providers", 877 | "refId": "B" 878 | } 879 | ], 880 | "thresholds": [], 881 | "timeFrom": null, 882 | "timeShift": null, 883 | "title": "OpenSTF devices", 884 | "tooltip": { 885 | "shared": true, 886 | "sort": 0, 887 | "value_type": "individual" 888 | }, 889 | "type": "graph", 890 | "xaxis": { 891 | "buckets": null, 892 | "mode": "time", 893 | "name": null, 894 | "show": true, 895 | "values": [] 896 | }, 897 | "yaxes": [ 898 | { 899 | "decimals": 0, 900 | "format": "short", 901 | "label": null, 902 | "logBase": 1, 903 | "max": null, 904 | "min": null, 905 | "show": true 906 | }, 907 | { 908 | "format": "short", 909 | "label": null, 910 | "logBase": 1, 911 | "max": null, 912 | "min": null, 913 | "show": true 914 | } 915 | ] 916 | }, 917 | { 918 | "aliasColors": {}, 919 | "bars": true, 920 | "dashLength": 10, 921 | "dashes": false, 922 | "datasource": "$datasource", 923 | "fill": 1, 924 | "id": 8, 925 | "legend": { 926 | "avg": false, 927 | "current": false, 928 | "max": false, 929 | "min": false, 930 | "show": false, 931 | "total": false, 932 | "values": false 933 | }, 934 | "lines": false, 935 | "linewidth": 1, 936 | "links": [], 937 | "nullPointMode": "null", 938 | "percentage": false, 939 | "pointradius": 5, 940 | "points": false, 941 | "renderer": "flot", 942 | "seriesOverrides": [], 943 | "spaceLength": 10, 944 | "span": 3, 945 | "stack": false, 946 | "steppedLine": false, 947 | "targets": [ 948 | { 949 | "expr": "count(openstf_status) by (notes)", 950 | "format": "time_series", 951 | "intervalFactor": 2, 952 | "legendFormat": "{{notes}}", 953 | "refId": "A" 954 | } 955 | ], 956 | "thresholds": [], 957 | "timeFrom": null, 958 | "timeShift": null, 959 | "title": "Notes", 960 | "tooltip": { 961 | "shared": false, 962 | "sort": 0, 963 | "value_type": "individual" 964 | }, 965 | "type": "graph", 966 | "xaxis": { 967 | "buckets": null, 968 | "mode": "series", 969 | "name": null, 970 | "show": true, 971 | "values": [ 972 | "current" 973 | ] 974 | }, 975 | "yaxes": [ 976 | { 977 | "format": "short", 978 | "label": null, 979 | "logBase": 1, 980 | "max": null, 981 | "min": null, 982 | "show": true 983 | }, 984 | { 985 | "format": "short", 986 | "label": null, 987 | "logBase": 1, 988 | "max": null, 989 | "min": null, 990 | "show": true 991 | } 992 | ] 993 | } 994 | ], 995 | "repeat": null, 996 | "repeatIteration": null, 997 | "repeatRowId": null, 998 | "showTitle": false, 999 | "title": "Dashboard Row", 1000 | "titleSize": "h6" 1001 | } 1002 | ], 1003 | "schemaVersion": 14, 1004 | "style": "dark", 1005 | "tags": [], 1006 | "templating": { 1007 | "list": [ 1008 | { 1009 | "current": { 1010 | "text": "prometheus", 1011 | "value": "prometheus" 1012 | }, 1013 | "hide": 0, 1014 | "label": null, 1015 | "name": "datasource", 1016 | "options": [], 1017 | "query": "prometheus", 1018 | "refresh": 1, 1019 | "regex": "", 1020 | "type": "datasource" 1021 | } 1022 | ] 1023 | }, 1024 | "time": { 1025 | "from": "now-12h", 1026 | "to": "now" 1027 | }, 1028 | "timepicker": { 1029 | "refresh_intervals": [ 1030 | "5s", 1031 | "10s", 1032 | "30s", 1033 | "1m", 1034 | "5m", 1035 | "15m", 1036 | "30m", 1037 | "1h", 1038 | "2h", 1039 | "1d" 1040 | ], 1041 | "time_options": [ 1042 | "5m", 1043 | "15m", 1044 | "1h", 1045 | "6h", 1046 | "12h", 1047 | "24h", 1048 | "2d", 1049 | "7d", 1050 | "30d" 1051 | ] 1052 | }, 1053 | "timezone": "", 1054 | "title": "OpenSTF", 1055 | "version": 38 1056 | } 1057 | -------------------------------------------------------------------------------- /openstf/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: Control and manage Android devices from your browser 3 | name: openstf 4 | version: 1.0.0 5 | keywords: 6 | - OpenSTF 7 | - Android 8 | -------------------------------------------------------------------------------- /openstf/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | */}} 13 | {{- define "fullname" -}} 14 | {{- $name := default .Chart.Name .Values.nameOverride -}} 15 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 16 | {{- end -}} 17 | -------------------------------------------------------------------------------- /openstf/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: "{{ template "fullname" . }}-common-environment-variables" 5 | labels: 6 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 7 | data: 8 | ZMQ_TCP_KEEPALIVE: "1" 9 | ZMQ_TCP_KEEPALIVE_IDLE: "60" 10 | --- 11 | apiVersion: v1 12 | kind: ConfigMap 13 | metadata: 14 | name: "{{ template "fullname" . }}-nginx-config" 15 | labels: 16 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 17 | data: 18 | nginx.conf: | 19 | worker_processes {{ .Values.nginx.resources.limits.cpu }}; 20 | events { 21 | worker_connections 1024; 22 | } 23 | 24 | http { 25 | default_type application/octet-stream; 26 | 27 | upstream stf_app { 28 | server {{ template "fullname" . }}-app:3100 max_fails=3; 29 | } 30 | 31 | upstream stf_auth { 32 | server {{ template "fullname" . }}-auth:3200 max_fails=3; 33 | } 34 | 35 | upstream stf_storage_apk { 36 | server {{ template "fullname" . }}-apk-storage:3300 max_fails=3; 37 | } 38 | 39 | upstream stf_storage_image { 40 | server {{ template "fullname" . }}-img-storage:3400 max_fails=3; 41 | } 42 | 43 | upstream stf_storage { 44 | server {{ template "fullname" . }}-storage:3500 max_fails=3; 45 | } 46 | 47 | upstream stf_websocket { 48 | server {{ template "fullname" . }}-websocket:3600 max_fails=3; 49 | } 50 | 51 | upstream stf_api { 52 | server {{ template "fullname" . }}-api:3700 max_fails=3; 53 | } 54 | 55 | types { 56 | application/javascript js; 57 | image/gif gif; 58 | image/jpeg jpg; 59 | text/css css; 60 | text/html html; 61 | } 62 | 63 | map $http_upgrade $connection_upgrade { 64 | default upgrade; 65 | '' close; 66 | } 67 | 68 | server { 69 | listen 80 default_server; 70 | listen [::]:80 default_server; 71 | server_name {{ .Values.ingress.hostname }}; 72 | keepalive_timeout 70; 73 | root /dev/null; 74 | 75 | resolver {{ .Values.dns.resolver }} valid=300s; 76 | resolver_timeout 10s; 77 | 78 | # Provider 79 | location ~ "^/(?[0-9\.]+)([^/]*)/(?[0-9]{5})/$" { 80 | proxy_pass http://$ip:$port/; 81 | proxy_http_version 1.1; 82 | proxy_set_header Upgrade $http_upgrade; 83 | proxy_set_header Connection $connection_upgrade; 84 | proxy_set_header X-Forwarded-For $remote_addr; 85 | proxy_set_header X-Real-IP $remote_addr; 86 | } 87 | 88 | location /s/image/ { 89 | proxy_pass http://stf_storage_image; 90 | } 91 | 92 | location /s/apk/ { 93 | proxy_pass http://stf_storage_apk; 94 | } 95 | 96 | location /s/ { 97 | client_max_body_size 1024m; 98 | client_body_buffer_size 128k; 99 | proxy_pass http://stf_storage; 100 | } 101 | 102 | location /socket.io/ { 103 | proxy_pass http://stf_websocket; 104 | proxy_http_version 1.1; 105 | proxy_set_header Upgrade $http_upgrade; 106 | proxy_set_header Connection $connection_upgrade; 107 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 108 | proxy_set_header X-Real-IP $http_x_real_ip; 109 | } 110 | 111 | location /auth/ { 112 | proxy_pass http://stf_auth/auth/; 113 | } 114 | 115 | location /api/ { 116 | proxy_pass http://stf_api/api/; 117 | } 118 | 119 | location / { 120 | proxy_pass http://stf_app; 121 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 122 | proxy_set_header X-Real-IP $http_x_real_ip; 123 | } 124 | } 125 | } 126 | 127 | --- 128 | 129 | apiVersion: v1 130 | kind: ConfigMap 131 | metadata: 132 | name: "{{ template "fullname" . }}-telegraf-config" 133 | labels: 134 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 135 | data: 136 | telegraf.conf: |+ 137 | {{- if .Values.telegraf.config.global_tags }} 138 | [global_tags] 139 | {{- range $key, $val := .Values.telegraf.config.global_tags }} 140 | {{ $key }} = {{ $val | quote }} 141 | {{- end }} 142 | {{- end }} 143 | [agent] 144 | interval = {{ .Values.telegraf.config.agent.interval | quote }} 145 | round_interval = {{ .Values.telegraf.config.agent.round_interval }} 146 | metric_buffer_limit = {{ .Values.telegraf.config.agent.metric_buffer_limit }} 147 | collection_jitter = {{ .Values.telegraf.config.agent.collection_jitter | quote }} 148 | flush_interval = {{ .Values.telegraf.config.agent.flush_interval | quote }} 149 | flush_jitter = {{ .Values.telegraf.config.agent.flush_jitter | quote }} 150 | precision = "" 151 | debug = true 152 | quiet = false 153 | logfile = "" 154 | hostname = "$HOST_IP" 155 | omit_hostname = false 156 | [[inputs.exec]] 157 | commands = ["cat /custom-metrics/devices"] 158 | data_format = "influx" 159 | [[inputs.exec]] 160 | commands = ["cat /custom-metrics/battery"] 161 | data_format = "influx" 162 | {{- if .Values.telegraf.config.outputs.graphite.enabled }} 163 | [[outputs.graphite]] 164 | servers = [{{ .Values.telegraf.config.outputs.graphite.server | quote }}] 165 | prefix = {{ .Values.telegraf.config.outputs.graphite.prefix | quote }} 166 | template = {{ .Values.telegraf.config.outputs.graphite.template | quote }} 167 | timeout = {{ .Values.telegraf.config.outputs.graphite.timeout }} 168 | {{- end }} 169 | {{- if .Values.telegraf.config.outputs.prometheus.enabled }} 170 | [[outputs.prometheus_client]] 171 | listen = ":9273" 172 | expiration_interval = "60s" 173 | collectors_exclude = ["gocollector", "process"] 174 | {{- end }} 175 | --- 176 | 177 | apiVersion: v1 178 | kind: ConfigMap 179 | metadata: 180 | name: "{{ template "fullname" . }}-telegraf-openstf-config" 181 | labels: 182 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 183 | data: 184 | crontab: |+ 185 | * * * * * /bin/bash /scripts/metrics.sh > /custom-metrics/openstf 186 | metrics.sh: |+ 187 | #!/usr/bin/env bash 188 | 189 | json_filter="openstf" 190 | 191 | declare -A metrics 192 | metrics['abi']='' 193 | metrics['battery.health']='' 194 | metrics['manufacturer']='string' 195 | metrics['model']='string' 196 | metrics['network.connected']='boolean' 197 | metrics['network.type']='' 198 | metrics['notes']='string' 199 | metrics['phone.imei']='string' 200 | metrics['present']='boolean' 201 | metrics['product']='string' 202 | metrics['ready']='boolean' 203 | metrics['remoteConnect']='boolean' 204 | metrics['serial']='string' 205 | metrics['sdk']='int' 206 | metrics['version']='string' 207 | metrics['using']='boolean' 208 | 209 | for metric in "${!metrics[@]}" 210 | do 211 | type="${metrics[$metric]}" 212 | metric_tag=$(echo "$metric" | sed -r 's/\./-/g') 213 | case $type in 214 | string) 215 | json_filter="$json_filter,${metric_tag,,}=\\\"\(.$metric)\\\"" 216 | ;; 217 | *) 218 | json_filter="$json_filter,${metric_tag,,}=\(.$metric)" 219 | ;; 220 | esac 221 | done 222 | 223 | q=".devices[] | \"$json_filter status=\(.status)\"" 224 | 225 | curl -s -H "Authorization: Bearer $STF_TOKEN" $STF_URL | jq -r "$q" > /tmp/stf.json 226 | 227 | while read line; do 228 | echo $line | rev | sed -r 's/ / \\/2g' | rev | awk '{print $0, systime()}' 229 | done < /tmp/stf.json 230 | telegraf.conf: |+ 231 | {{- if .Values.telegraf.config.global_tags }} 232 | [global_tags] 233 | {{- range $key, $val := .Values.telegraf.config.global_tags }} 234 | {{ $key }} = {{ $val | quote }} 235 | {{- end }} 236 | {{- end }} 237 | [agent] 238 | interval = {{ .Values.telegraf.config.agent.interval | quote }} 239 | round_interval = {{ .Values.telegraf.config.agent.round_interval }} 240 | metric_buffer_limit = {{ .Values.telegraf.config.agent.metric_buffer_limit }} 241 | collection_jitter = {{ .Values.telegraf.config.agent.collection_jitter | quote }} 242 | flush_interval = {{ .Values.telegraf.config.agent.flush_interval | quote }} 243 | flush_jitter = {{ .Values.telegraf.config.agent.flush_jitter | quote }} 244 | precision = "" 245 | debug = true 246 | quiet = false 247 | logfile = "" 248 | omit_hostname = true 249 | [[inputs.exec]] 250 | commands = ["cat /custom-metrics/openstf"] 251 | data_format = "influx" 252 | {{- if .Values.telegraf.config.outputs.graphite.enabled }} 253 | [[outputs.graphite]] 254 | servers = [{{ .Values.telegraf.config.outputs.graphite.server | quote }}] 255 | prefix = {{ .Values.telegraf.config.outputs.graphite.prefix | quote }} 256 | template = {{ .Values.telegraf.config.outputs.graphite.template | quote }} 257 | timeout = {{ .Values.telegraf.config.outputs.graphite.timeout }} 258 | {{- end }} 259 | {{- if .Values.telegraf.config.outputs.prometheus.enabled }} 260 | [[outputs.prometheus_client]] 261 | listen = ":9273" 262 | expiration_interval = "60s" 263 | collectors_exclude = ["gocollector", "process"] 264 | {{- end }} 265 | -------------------------------------------------------------------------------- /openstf/templates/daemonset.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: DaemonSet 3 | metadata: 4 | name: {{ template "fullname" . }}-provider-real 5 | labels: 6 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 7 | app: {{ template "fullname" . }}-provider-real 8 | spec: 9 | template: 10 | metadata: 11 | {{- if .Values.telegraf.config.outputs.prometheus.enabled }} 12 | annotations: 13 | prometheus.io/scrape: "true" 14 | prometheus.io/port: "9273" 15 | {{- end }} 16 | labels: 17 | app: {{ template "fullname" . }}-provider-real 18 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 19 | release: "{{ .Release.Name }}" 20 | heritage: "{{ .Release.Service }}" 21 | spec: 22 | containers: 23 | - image: "{{ .Values.adb.image.repository }}:{{ .Values.adb.image.tag }}" 24 | name: adb-butler 25 | imagePullPolicy: {{ .Values.adb.image.pullPolicy }} 26 | env: 27 | - name: RETHINKDB_URL 28 | value: "{{ .Values.db.url }}" 29 | - name: RETHINKDB_PORT 30 | value: "{{ .Values.db.port }}" 31 | - name: RETHINKDB_ENV_AUTHKEY 32 | value: "{{ .Values.db.password }}" 33 | - name: GNIREHTET_ENABLED 34 | value: "{{ .Values.provider.real.tethering.enabled }}" 35 | - name: DNS_SERVER 36 | value: "{{ .Values.provider.real.tethering.dns }}" 37 | resources: 38 | {{ toYaml .Values.adb.resources | indent 12 }} 39 | securityContext: 40 | privileged: true 41 | volumeMounts: 42 | - mountPath: /dev 43 | name: dev 44 | - mountPath: /sys 45 | name: sys 46 | - mountPath: /custom-metrics 47 | name: custom-metrics 48 | - name: telegraf 49 | image: "{{ .Values.telegraf.image.repository }}:{{ .Values.telegraf.image.tag }}" 50 | imagePullPolicy: {{ .Values.telegraf.image.pullPolicy }} 51 | env: 52 | - name: HOST_IP 53 | valueFrom: 54 | fieldRef: 55 | fieldPath: status.hostIP 56 | resources: 57 | {{ toYaml .Values.telegraf.resources | indent 12 }} 58 | volumeMounts: 59 | - name: telegraf-config-volume 60 | mountPath: /etc/telegraf 61 | - name: custom-metrics 62 | mountPath: /custom-metrics 63 | - name: stf-provider 64 | image: {{ .Values.stf.image.repository }}:{{ .Values.stf.image.tag }} 65 | args: 66 | - /bin/sh 67 | - -c 68 | - sleep 10 && stf provider --connect-sub "tcp://{{ template "fullname" . }}-triproxy-dev:7250" --connect-push "tcp://{{ template "fullname" . }}-triproxy-dev:7270" --min-port "15000" --max-port "25000" --heartbeat-interval "10000" --storage-url "https://{{ .Values.ingress.hostname }}/" --screen-ws-url-pattern "wss://{{ .Values.ingress.hostname }}/$(STF_PROVIDER_PUBLIC_IP)/<%= publicPort %>/" --allow-remote --cleanup "false" 2>&1 | tee /var/log/stf/messages 69 | env: 70 | - name: RETHINKDB_PORT_28015_TCP 71 | value: "tcp://{{ .Values.db.url }}:{{ .Values.db.port }}" 72 | - name: RETHINKDB_ENV_AUTHKEY 73 | value: "{{ .Values.db.password }}" 74 | - name: STF_PROVIDER_PUBLIC_IP 75 | valueFrom: 76 | fieldRef: 77 | apiVersion: v1 78 | fieldPath: status.podIP 79 | envFrom: 80 | - configMapRef: 81 | name: "{{ template "fullname" . }}-common-environment-variables" 82 | volumeMounts: 83 | - mountPath: /var/log/stf 84 | name: logs 85 | resources: 86 | {{ toYaml .Values.provider.real.resources | indent 12 }} 87 | nodeSelector: 88 | openstf.io/provider: "true" 89 | {{- if .Values.androidDevicesTolerations }} 90 | tolerations: 91 | {{ toYaml .Values.androidDevicesTolerations | indent 8 }} 92 | {{- end }} 93 | volumes: 94 | - configMap: 95 | defaultMode: 420 96 | name: "{{ template "fullname" . }}-telegraf-config" 97 | name: telegraf-config-volume 98 | - name: dev 99 | hostPath: 100 | path: /dev 101 | - name: sys 102 | hostPath: 103 | path: /sys 104 | - name: custom-metrics 105 | emptyDir: {} 106 | - name: logs 107 | emptyDir: {} 108 | imagePullSecrets: 109 | - name: {{ .Values.pullSecret }} 110 | -------------------------------------------------------------------------------- /openstf/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: {{ template "fullname" . }}-api 5 | spec: 6 | replicas: {{ .Values.stf.api.replicas }} 7 | template: 8 | metadata: 9 | labels: 10 | app: {{ template "fullname" . }}-api 11 | spec: 12 | containers: 13 | - name: api 14 | image: {{ .Values.stf.image.repository }}:{{ .Values.stf.image.tag }} 15 | imagePullPolicy: {{ .Values.stf.pullPolicy }} 16 | command: ["stf"] 17 | args: 18 | - api 19 | - --secret 20 | - "nosecret" 21 | - --port 22 | - "3000" 23 | - --connect-sub 24 | - "tcp://{{ template "fullname" . }}-triproxy-app:7150" 25 | - --connect-push 26 | - "tcp://{{ template "fullname" . }}-triproxy-app:7170" 27 | env: 28 | - name: RETHINKDB_PORT_28015_TCP 29 | value: "tcp://{{.Values.db.url}}:{{.Values.db.port}}" 30 | - name: RETHINKDB_ENV_AUTHKEY 31 | value: "{{.Values.db.password}}" 32 | ports: 33 | - containerPort: 3000 34 | resources: 35 | {{ toYaml .Values.stf.api.resources | indent 10 }} 36 | imagePullSecrets: 37 | - name: {{ .Values.pullSecret }} 38 | --- 39 | apiVersion: extensions/v1beta1 40 | kind: Deployment 41 | metadata: 42 | name: {{ template "fullname" . }}-app 43 | spec: 44 | replicas: {{ .Values.stf.app.replicas }} 45 | template: 46 | metadata: 47 | labels: 48 | app: {{ template "fullname" . }}-app 49 | spec: 50 | containers: 51 | - name: app 52 | image: {{ .Values.stf.image.repository }}:{{ .Values.stf.image.tag }} 53 | imagePullPolicy: {{ .Values.stf.pullPolicy }} 54 | command: ["stf"] 55 | args: 56 | - "app" 57 | - --secret 58 | - "nosecret" 59 | - --port 60 | - "3000" 61 | - --auth-url 62 | - "https://{{ .Values.ingress.hostname }}/auth/mock/" 63 | - --websocket-url 64 | - "https://{{ .Values.ingress.hostname }}/" 65 | env: 66 | - name: RETHINKDB_PORT_28015_TCP 67 | value: "tcp://{{.Values.db.url}}:{{.Values.db.port}}" 68 | - name: RETHINKDB_ENV_AUTHKEY 69 | value: "{{.Values.db.password}}" 70 | envFrom: 71 | - configMapRef: 72 | name: "{{ template "fullname" . }}-common-environment-variables" 73 | ports: 74 | - containerPort: 3000 75 | name: bind-port 76 | resources: 77 | {{ toYaml .Values.stf.api.resources | indent 10 }} 78 | imagePullSecrets: 79 | - name: {{ .Values.pullSecret }} 80 | --- 81 | apiVersion: extensions/v1beta1 82 | kind: Deployment 83 | metadata: 84 | name: {{ template "fullname" . }}-auth 85 | spec: 86 | replicas: {{ .Values.stf.auth.replicas }} 87 | template: 88 | metadata: 89 | labels: 90 | app: {{ template "fullname" . }}-auth 91 | spec: 92 | containers: 93 | - name: auth 94 | image: {{ .Values.stf.image.repository }}:{{ .Values.stf.image.tag }} 95 | imagePullPolicy: {{ .Values.stf.pullPolicy }} 96 | command: ["stf"] 97 | args: 98 | - auth-mock 99 | - --secret 100 | - "nosecret" 101 | - --port 102 | - "3000" 103 | - --app-url 104 | - "https://{{ .Values.ingress.hostname }}/" 105 | ports: 106 | - containerPort: 3000 107 | envFrom: 108 | - configMapRef: 109 | name: "{{ template "fullname" . }}-common-environment-variables" 110 | resources: 111 | {{ toYaml .Values.stf.auth.resources | indent 10 }} 112 | imagePullSecrets: 113 | - name: {{ .Values.pullSecret }} 114 | --- 115 | apiVersion: extensions/v1beta1 116 | kind: Deployment 117 | metadata: 118 | name: {{ template "fullname" . }}-processor 119 | spec: 120 | replicas: {{ .Values.stf.processor.replicas }} 121 | template: 122 | metadata: 123 | labels: 124 | app: {{ template "fullname" . }}-processor 125 | spec: 126 | containers: 127 | - name: processor 128 | image: {{ .Values.stf.image.repository }}:{{ .Values.stf.image.tag }} 129 | imagePullPolicy: {{ .Values.stf.pullPolicy }} 130 | command: ["stf"] 131 | args: 132 | - processor 133 | - "stf-processor" 134 | - --connect-app-dealer 135 | - "tcp://{{ template "fullname" . }}-triproxy-app:7160" 136 | - --connect-dev-dealer 137 | - "tcp://{{ template "fullname" . }}-triproxy-dev:7260" 138 | env: 139 | - name: RETHINKDB_PORT_28015_TCP 140 | value: "tcp://{{.Values.db.url}}:{{.Values.db.port}}" 141 | - name: RETHINKDB_ENV_AUTHKEY 142 | value: "{{.Values.db.password}}" 143 | envFrom: 144 | - configMapRef: 145 | name: "{{ template "fullname" . }}-common-environment-variables" 146 | resources: 147 | {{ toYaml .Values.stf.processor.resources | indent 10 }} 148 | imagePullSecrets: 149 | - name: {{ .Values.pullSecret }} 150 | --- 151 | apiVersion: extensions/v1beta1 152 | kind: Deployment 153 | metadata: 154 | name: {{ template "fullname" . }}-reaper 155 | spec: 156 | replicas: {{ .Values.stf.reaper.replicas}} 157 | template: 158 | metadata: 159 | labels: 160 | app: {{ template "fullname" . }}-reaper 161 | spec: 162 | containers: 163 | - name: reaper 164 | image: {{ .Values.stf.image.repository }}:{{ .Values.stf.image.tag }} 165 | imagePullPolicy: {{ .Values.stf.pullPolicy }} 166 | command: ["stf"] 167 | args: 168 | - reaper 169 | - "dev" 170 | - --connect-push 171 | - "tcp://{{ template "fullname" . }}-triproxy-dev:7270" 172 | - --connect-sub 173 | - "tcp://{{ template "fullname" . }}-triproxy-app:7150" 174 | - --heartbeat-timeout 175 | - "30000" 176 | env: 177 | - name: RETHINKDB_PORT_28015_TCP 178 | value: "tcp://{{.Values.db.url}}:{{.Values.db.port}}" 179 | - name: RETHINKDB_ENV_AUTHKEY 180 | value: "{{.Values.db.password}}" 181 | envFrom: 182 | - configMapRef: 183 | name: "{{ template "fullname" . }}-common-environment-variables" 184 | resources: 185 | {{ toYaml .Values.stf.reaper.resources | indent 10 }} 186 | imagePullSecrets: 187 | - name: {{ .Values.pullSecret }} 188 | --- 189 | apiVersion: extensions/v1beta1 190 | kind: Deployment 191 | metadata: 192 | name: {{ template "fullname" . }}-apk-storage 193 | spec: 194 | replicas: {{ .Values.stf.apkStorage.replicas }} 195 | template: 196 | metadata: 197 | labels: 198 | app: {{ template "fullname" . }}-apk-storage 199 | spec: 200 | containers: 201 | - name: apk-storage 202 | image: {{ .Values.stf.image.repository }}:{{ .Values.stf.image.tag }} 203 | imagePullPolicy: {{ .Values.stf.pullPolicy }} 204 | command: ["stf"] 205 | args: 206 | - storage-plugin-apk 207 | - --port 208 | - "3000" 209 | - --storage-url 210 | - "https://{{ .Values.ingress.hostname }}/" 211 | envFrom: 212 | - configMapRef: 213 | name: "{{ template "fullname" . }}-common-environment-variables" 214 | resources: 215 | {{ toYaml .Values.stf.apkStorage.resources | indent 10 }} 216 | imagePullSecrets: 217 | - name: {{ .Values.pullSecret }} 218 | --- 219 | apiVersion: extensions/v1beta1 220 | kind: Deployment 221 | metadata: 222 | name: {{ template "fullname" . }}-img-storage 223 | spec: 224 | replicas: {{ .Values.stf.imgStorage.replicas }} 225 | template: 226 | metadata: 227 | labels: 228 | app: {{ template "fullname" . }}-img-storage 229 | spec: 230 | containers: 231 | - name: img-storage 232 | image: {{ .Values.stf.image.repository }}:{{ .Values.stf.image.tag }} 233 | imagePullPolicy: {{ .Values.stf.pullPolicy }} 234 | command: ["stf"] 235 | args: 236 | - storage-plugin-image 237 | - --port 238 | - "3000" 239 | - --storage-url 240 | - "https://{{ .Values.ingress.hostname }}/" 241 | envFrom: 242 | - configMapRef: 243 | name: "{{ template "fullname" . }}-common-environment-variables" 244 | resources: 245 | {{ toYaml .Values.stf.imgStorage.resources | indent 10 }} 246 | imagePullSecrets: 247 | - name: {{ .Values.pullSecret }} 248 | --- 249 | apiVersion: extensions/v1beta1 250 | kind: Deployment 251 | metadata: 252 | name: {{ template "fullname" . }}-storage 253 | spec: 254 | replicas: {{ .Values.stf.storage.replicas }} 255 | template: 256 | metadata: 257 | labels: 258 | app: {{ template "fullname" . }}-storage 259 | spec: 260 | containers: 261 | - name: storage 262 | image: {{ .Values.stf.image.repository }}:{{ .Values.stf.image.tag }} 263 | imagePullPolicy: {{ .Values.stf.pullPolicy }} 264 | command: ["stf"] 265 | args: 266 | - storage-temp 267 | - --port 268 | - "3000" 269 | - --save-dir 270 | - "/data" 271 | ports: 272 | - containerPort: 3000 273 | volumeMounts: 274 | - mountPath: /data 275 | name: storage 276 | envFrom: 277 | - configMapRef: 278 | name: "{{ template "fullname" . }}-common-environment-variables" 279 | resources: 280 | {{ toYaml .Values.stf.storage.resources | indent 10 }} 281 | volumes: 282 | - name: storage 283 | emptyDir: {} 284 | imagePullSecrets: 285 | - name: {{ .Values.pullSecret }} 286 | --- 287 | apiVersion: extensions/v1beta1 288 | kind: Deployment 289 | metadata: 290 | name: {{ template "fullname" . }}-triproxy-dev 291 | spec: 292 | replicas: {{ .Values.stf.triproxyDev.replicas }} 293 | template: 294 | metadata: 295 | labels: 296 | app: {{ template "fullname" . }}-triproxy-dev 297 | spec: 298 | containers: 299 | - name: triproxy-dev 300 | image: {{ .Values.stf.image.repository }}:{{ .Values.stf.image.tag }} 301 | imagePullPolicy: {{ .Values.stf.pullPolicy }} 302 | command: ["stf"] 303 | args: 304 | - triproxy 305 | - "dev" 306 | - --bind-pub 307 | - "tcp://*:7250" 308 | - --bind-dealer 309 | - "tcp://*:7260" 310 | - --bind-pull 311 | - "tcp://*:7270" 312 | ports: 313 | - containerPort: 7250 314 | name: bind-pub 315 | - containerPort: 7260 316 | name: bind-dealer 317 | - containerPort: 7270 318 | name: bind-pull 319 | envFrom: 320 | - configMapRef: 321 | name: "{{ template "fullname" . }}-common-environment-variables" 322 | resources: 323 | {{ toYaml .Values.stf.triproxyDev.resources | indent 10 }} 324 | imagePullSecrets: 325 | - name: {{ .Values.pullSecret }} 326 | --- 327 | apiVersion: extensions/v1beta1 328 | kind: Deployment 329 | metadata: 330 | name: {{ template "fullname" . }}-triproxy-app 331 | spec: 332 | replicas: {{ .Values.stf.triproxyApp.replicas }} 333 | template: 334 | metadata: 335 | labels: 336 | app: {{ template "fullname" . }}-triproxy-app 337 | spec: 338 | containers: 339 | - name: triproxy-app 340 | image: {{ .Values.stf.image.repository }}:{{ .Values.stf.image.tag }} 341 | imagePullPolicy: {{ .Values.stf.pullPolicy }} 342 | command: ["stf"] 343 | args: 344 | - triproxy 345 | - "app" 346 | - --bind-pub 347 | - "tcp://*:7150" 348 | - --bind-dealer 349 | - "tcp://*:7160" 350 | - --bind-pull 351 | - "tcp://*:7170" 352 | ports: 353 | - containerPort: 7150 354 | name: bind-pub 355 | - containerPort: 7160 356 | name: bind-dealer 357 | - containerPort: 7170 358 | name: bind-pull 359 | envFrom: 360 | - configMapRef: 361 | name: "{{ template "fullname" . }}-common-environment-variables" 362 | resources: 363 | {{ toYaml .Values.stf.triproxyApp.resources | indent 10 }} 364 | imagePullSecrets: 365 | - name: {{ .Values.pullSecret }} 366 | --- 367 | apiVersion: extensions/v1beta1 368 | kind: Deployment 369 | metadata: 370 | name: {{ template "fullname" . }}-websocket 371 | spec: 372 | replicas: {{ .Values.stf.websocket.replicas }} 373 | template: 374 | metadata: 375 | labels: 376 | app: {{ template "fullname" . }}-websocket 377 | spec: 378 | containers: 379 | - name: websocket 380 | image: {{ .Values.stf.image.repository }}:{{ .Values.stf.image.tag }} 381 | imagePullPolicy: {{ .Values.stf.pullPolicy }} 382 | command: ["stf"] 383 | args: 384 | - websocket 385 | - --secret 386 | - "nosecret" 387 | - --port 388 | - "3000" 389 | - --storage-url 390 | - "https://{{ .Values.ingress.hostname }}/" 391 | - --connect-sub 392 | - "tcp://{{ template "fullname" . }}-triproxy-app:7150" 393 | - --connect-push 394 | - "tcp://{{ template "fullname" . }}-triproxy-app:7170" 395 | env: 396 | - name: RETHINKDB_PORT_28015_TCP 397 | value: "tcp://{{.Values.db.url}}:{{.Values.db.port}}" 398 | - name: RETHINKDB_ENV_AUTHKEY 399 | value: "{{.Values.db.password}}" 400 | envFrom: 401 | - configMapRef: 402 | name: "{{ template "fullname" . }}-common-environment-variables" 403 | ports: 404 | - containerPort: 3000 405 | resources: 406 | {{ toYaml .Values.stf.websocket.resources | indent 10 }} 407 | imagePullSecrets: 408 | - name: {{ .Values.pullSecret }} 409 | --- 410 | apiVersion: extensions/v1beta1 411 | kind: Deployment 412 | metadata: 413 | name: {{ template "fullname" . }}-nginx 414 | spec: 415 | replicas: {{ .Values.nginx.replicas }} 416 | template: 417 | metadata: 418 | labels: 419 | app: {{ template "fullname" . }}-nginx 420 | spec: 421 | containers: 422 | - name: nginx 423 | image: {{ .Values.nginx.image.repository }}:{{ .Values.nginx.image.tag }} 424 | imagePullPolicy: {{ .Values.nginx.image.pullPolicy }} 425 | ports: 426 | - name: http 427 | containerPort: 80 428 | volumeMounts: 429 | - mountPath: /etc/nginx/nginx.conf 430 | subPath: nginx.conf 431 | name: nginx-conf-volume 432 | envFrom: 433 | - configMapRef: 434 | name: "{{ template "fullname" . }}-common-environment-variables" 435 | resources: 436 | {{ toYaml .Values.nginx.resources | indent 12 }} 437 | imagePullSecrets: 438 | - name: {{ .Values.pullSecret }} 439 | volumes: 440 | - name: nginx-conf-volume 441 | configMap: 442 | name: "{{ template "fullname" . }}-nginx-config" 443 | items: 444 | - key: nginx.conf 445 | path: nginx.conf 446 | --- 447 | apiVersion: extensions/v1beta1 448 | kind: Deployment 449 | metadata: 450 | name: {{ template "fullname" . }}-telegraf 451 | spec: 452 | replicas: 1 453 | template: 454 | metadata: 455 | {{- if .Values.telegraf.config.outputs.prometheus.enabled }} 456 | annotations: 457 | prometheus.io/scrape: "true" 458 | prometheus.io/port: "9273" 459 | {{- end }} 460 | labels: 461 | app: {{ template "fullname" . }}-telegraf 462 | spec: 463 | containers: 464 | - name: metrics 465 | image: "{{ .Values.metrics.image.repository }}:{{ .Values.metrics.image.tag }}" 466 | imagePullPolicy: {{ .Values.metrics.image.pullPolicy }} 467 | env: 468 | - name: STF_URL 469 | value: http://{{ template "fullname" . }}-api:3700/api/v1/devices 470 | - name: STF_TOKEN 471 | value: "{{ .Values.metrics.token }}" 472 | resources: 473 | {{ toYaml .Values.metrics.resources | indent 12 }} 474 | volumeMounts: 475 | - name: cron-config 476 | mountPath: /var/spool/cron/crontabs/ 477 | - name: metrics-script 478 | mountPath: /scripts 479 | - name: custom-metrics 480 | mountPath: /custom-metrics 481 | - name: telegraf 482 | image: "{{ .Values.telegraf.image.repository }}:{{ .Values.telegraf.image.tag }}" 483 | resources: 484 | {{ toYaml .Values.telegraf.resources | indent 12 }} 485 | volumeMounts: 486 | - name: telegraf-config-volume 487 | mountPath: /etc/telegraf 488 | - name: custom-metrics 489 | mountPath: /custom-metrics 490 | volumes: 491 | - name: telegraf-config-volume 492 | configMap: 493 | defaultMode: 420 494 | name: "{{ template "fullname" . }}-telegraf-openstf-config" 495 | items: 496 | - key: telegraf.conf 497 | path: telegraf.conf 498 | - name: cron-config 499 | configMap: 500 | name: "{{ template "fullname" . }}-telegraf-openstf-config" 501 | items: 502 | - key: crontab 503 | path: root 504 | - name: metrics-script 505 | configMap: 506 | name: "{{ template "fullname" . }}-telegraf-openstf-config" 507 | items: 508 | - key: metrics.sh 509 | path: metrics.sh 510 | - name: custom-metrics 511 | emptyDir: {} 512 | imagePullSecrets: 513 | - name: {{ .Values.pullSecret }} 514 | -------------------------------------------------------------------------------- /openstf/templates/emulators.yaml: -------------------------------------------------------------------------------- 1 | {{- $root := . -}} 2 | {{- range $type := .Values.emulators.types }} 3 | {{- range $emu := .instances }} 4 | apiVersion: extensions/v1beta1 5 | kind: Deployment 6 | metadata: 7 | name: {{ template "fullname" $root }}-provider-{{ $type.name }}-{{ $emu.version }} 8 | spec: 9 | replicas: {{ $emu.count }} 10 | strategy: 11 | rollingUpdate: 12 | maxUnavailable: 25% 13 | maxSurge: 25% 14 | template: 15 | metadata: 16 | labels: 17 | app: {{ template "fullname" $root }}-provider-{{ $type.name }}-{{ $emu.version }} 18 | spec: 19 | shareProcessNamespace: true 20 | containers: 21 | - name: adb-butler 22 | image: {{ $root.Values.adb.image.repository }}:{{ $root.Values.adb.image.tag }} 23 | imagePullPolicy: {{ $root.Values.adb.image.pullPolicy }} 24 | resources: 25 | {{ toYaml $root.Values.adb.resources | indent 12 }} 26 | lifecycle: 27 | preStop: 28 | exec: 29 | command: [ "/usr/bin/node", "clean.js" ] 30 | securityContext: 31 | privileged: true 32 | env: 33 | - name: RETHINKDB_URL 34 | value: "{{ $root.Values.db.url }}" 35 | - name: RETHINKDB_PORT 36 | value: "{{ $root.Values.db.port }}" 37 | - name: RETHINKDB_ENV_AUTHKEY 38 | value: "{{ $root.Values.db.password }}" 39 | {{ if $type.timezone }} 40 | - name: EMULATOR_TIMEZONE 41 | value: "{{ $type.timezone }}" 42 | {{ end }} 43 | - name: STF_PROVIDER_PUBLIC_IP 44 | valueFrom: 45 | fieldRef: 46 | apiVersion: v1 47 | fieldPath: status.podIP 48 | - name: STF_PROVIDER_NOTE 49 | value: "{{ $type.note }}" 50 | envFrom: 51 | - configMapRef: 52 | name: "{{ template "fullname" $root }}-common-environment-variables" 53 | livenessProbe: 54 | exec: 55 | command: 56 | - "/bin/sh" 57 | - "-c" 58 | - echo -n | adb devices | egrep 'device$' | awk '{ print $1 }' | while read d; do echo -n | timeout -t 10 adb -s $d shell getprop sys.boot_completed; done | egrep '^\s*1\s*$' | wc -l | egrep -q '^2$' 59 | initialDelaySeconds: {{ $root.Values.provider.emulator.livenessProbe.initialDelaySeconds }} 60 | periodSeconds: {{ $root.Values.provider.emulator.livenessProbe.periodSeconds }} 61 | readinessProbe: 62 | exec: 63 | command: 64 | - "/bin/sh" 65 | - "-c" 66 | - echo -n | adb devices | egrep 'device$' | awk '{ print $1 }' | while read d; do echo -n | timeout -t 10 adb -s $d shell getprop sys.boot_completed; done | egrep '^\s*1\s*$' | wc -l | egrep -q '^2$' 67 | initialDelaySeconds: {{ $root.Values.provider.emulator.readinessProbe.initialDelaySeconds }} 68 | periodSeconds: {{ $root.Values.provider.emulator.readinessProbe.periodSeconds }} 69 | failureThreshold: {{ $root.Values.provider.emulator.readinessProbe.failureThreshold }} 70 | - name: stf-provider 71 | image: {{ $root.Values.stf.image.repository }}:{{ $root.Values.stf.image.tag }} 72 | imagePullPolicy: {{ $root.Values.stf.image.pullPolicy }} 73 | args: 74 | - /bin/sh 75 | - -c 76 | - sleep 10 && stf provider "$(STF_PROVIDER_PUBLIC_IP):10001" --connect-sub "tcp://{{ template "fullname" $root }}-triproxy-dev:7250" --connect-push "tcp://{{ template "fullname" $root }}-triproxy-dev:7270" --min-port "15000" --max-port "25000" --heartbeat-interval "10000" --storage-url "https://{{ $root.Values.ingress.hostname }}/" --screen-ws-url-pattern "wss://{{ $root.Values.ingress.hostname }}/<%= serial %>/<%= publicPort %>/" --allow-remote --cleanup "false" 2>&1 | tee /var/log/stf/messages 77 | env: 78 | - name: RETHINKDB_PORT_28015_TCP 79 | value: "tcp://{{ $root.Values.db.url }}:{{ $root.Values.db.port }}" 80 | - name: RETHINKDB_ENV_AUTHKEY 81 | value: "{{ $root.Values.db.password }}" 82 | - name: STF_PROVIDER_PUBLIC_IP 83 | valueFrom: 84 | fieldRef: 85 | apiVersion: v1 86 | fieldPath: status.podIP 87 | envFrom: 88 | - configMapRef: 89 | name: "{{ template "fullname" $root }}-common-environment-variables" 90 | livenessProbe: 91 | exec: 92 | command: 93 | - "/bin/sh" 94 | - "-c" 95 | - grep -q 'spawn adb ENOENT' /var/log/stf/messages && exit 1 || echo 'Everything ok' 96 | initialDelaySeconds: 5 97 | periodSeconds: 5 98 | resources: 99 | {{ toYaml $root.Values.provider.emulator.resources | indent 12 }} 100 | volumeMounts: 101 | - mountPath: /var/log/stf 102 | name: logs 103 | - name: emulator 104 | image: {{ $root.Values.emulators.imagePrefix }}{{ $emu.version }}:{{ $root.Values.emulators.imageVersion }} 105 | imagePullPolicy: {{ $root.Values.emulators.pullPolicy }} 106 | env: 107 | - name: ANDROID_ARCH 108 | value: "x86" 109 | - name: NAME 110 | value: "android-{{ $emu.version }}" 111 | - name: CONSOLE_PORT 112 | value: "10000" 113 | - name: ADB_PORT 114 | value: "10001" 115 | - name: ADB_SERVER_PORT 116 | value: "9999" 117 | - name: ANDROID_CONFIG 118 | value: "{{ $type.args }}" 119 | resources: 120 | {{ toYaml $root.Values.emulators.resources | indent 12 }} 121 | securityContext: 122 | privileged: true 123 | envFrom: 124 | - configMapRef: 125 | name: "{{ template "fullname" $root }}-common-environment-variables" 126 | livenessProbe: 127 | exec: 128 | command: 129 | - "/bin/sh" 130 | - "-c" 131 | - echo $(timeout 10s _adb -s emulator-10000 shell getprop sys.boot_completed) | grep -q '1' 132 | initialDelaySeconds: {{ $root.Values.provider.emulator.livenessProbe.initialDelaySeconds }} 133 | periodSeconds: {{ $root.Values.provider.emulator.livenessProbe.periodSeconds }} 134 | readinessProbe: 135 | exec: 136 | command: 137 | - "/bin/sh" 138 | - "-c" 139 | - echo $(timeout 10s _adb -s emulator-10000 shell getprop sys.boot_completed) | grep -q '1' 140 | initialDelaySeconds: {{ $root.Values.provider.emulator.readinessProbe.initialDelaySeconds }} 141 | periodSeconds: {{ $root.Values.provider.emulator.readinessProbe.periodSeconds }} 142 | failureThreshold: {{ $root.Values.provider.emulator.readinessProbe.failureThreshold }} 143 | volumeMounts: 144 | - mountPath: /dev/kvm 145 | name: dev-kvm 146 | nodeSelector: 147 | openstf.io/emulator: "kvm" 148 | volumes: 149 | - name: stf-adb 150 | hostPath: 151 | path: /dev/bus/usb 152 | - name: dev-kvm 153 | hostPath: 154 | path: /dev/kvm 155 | - name: logs 156 | emptyDir: {} 157 | imagePullSecrets: 158 | - name: {{ $root.Values.pullSecret }} 159 | --- 160 | {{- end }} 161 | {{- end }} 162 | -------------------------------------------------------------------------------- /openstf/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | apiVersion: extensions/v1beta1 3 | kind: Ingress 4 | metadata: 5 | name: {{ template "fullname" . }} 6 | annotations: 7 | {{ if .Values.ingress.annotations }} 8 | {{ toYaml .Values.ingress.annotations | indent 4 }} 9 | {{ else }} 10 | nginx.org/websocket-services: "{{ template "fullname" . }}-nginx" 11 | {{ end }} 12 | spec: 13 | {{ if .Values.ingress.ssl.enabled }} 14 | tls: 15 | - hosts: 16 | - {{ .Values.ingress.hostname }} 17 | secretName: {{ .Values.ingress.ssl.secret }} 18 | {{ end }} 19 | rules: 20 | - host: {{ .Values.ingress.hostname }} 21 | http: 22 | paths: 23 | - path: / 24 | backend: 25 | serviceName: {{ template "fullname" . }}-nginx 26 | servicePort: 80 27 | {{- end -}} 28 | -------------------------------------------------------------------------------- /openstf/templates/pre-install-job.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: {{.Release.Name}}-{{ randAlphaNum 5 | lower }} 5 | labels: 6 | heritage: {{.Release.Service | quote }} 7 | release: {{.Release.Name | quote }} 8 | chart: "{{.Chart.Name}}-{{.Chart.Version}}" 9 | annotations: 10 | "helm.sh/hook": pre-install,pre-upgrade 11 | "helm.sh/hook-weight": "-5" 12 | "helm.sh/hook-delete-policy": hook-succeeded 13 | spec: 14 | template: 15 | metadata: 16 | name: {{.Release.Name}} 17 | labels: 18 | heritage: {{.Release.Service | quote }} 19 | release: {{.Release.Name | quote }} 20 | chart: "{{.Chart.Name}}-{{.Chart.Version}}" 21 | spec: 22 | restartPolicy: Never 23 | containers: 24 | - name: migrate 25 | image: {{ .Values.stf.image.repository }}:{{ .Values.stf.image.tag }} 26 | imagePullPolicy: {{ .Values.stf.image.pullPolicy }} 27 | command: ["stf"] 28 | args: ["migrate"] 29 | env: 30 | - name: RETHINKDB_PORT_28015_TCP 31 | value: "tcp://{{.Values.db.url}}:{{.Values.db.port}}" 32 | - name: RETHINKDB_ENV_AUTHKEY 33 | value: "{{.Values.db.password}}" 34 | resources: 35 | {{ toYaml .Values.stf.migration.resources | indent 10 }} 36 | imagePullSecrets: 37 | - name: {{ .Values.pullSecret }} 38 | -------------------------------------------------------------------------------- /openstf/templates/svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: {{ template "fullname" . }}-nginx 6 | name: {{ template "fullname" . }}-nginx 7 | spec: 8 | ports: 9 | - port: 80 10 | targetPort: 80 11 | selector: 12 | app: {{ template "fullname" . }}-nginx 13 | --- 14 | apiVersion: v1 15 | kind: Service 16 | metadata: 17 | labels: 18 | app: {{ template "fullname" . }}-api 19 | name: {{ template "fullname" . }}-api 20 | spec: 21 | ports: 22 | - port: 3700 23 | targetPort: 3000 24 | selector: 25 | app: {{ template "fullname" . }}-api 26 | --- 27 | apiVersion: v1 28 | kind: Service 29 | metadata: 30 | labels: 31 | app: {{ template "fullname" . }}-app 32 | name: {{ template "fullname" . }}-app 33 | spec: 34 | ports: 35 | - port: 3100 36 | targetPort: 3000 37 | selector: 38 | app: {{ template "fullname" . }}-app 39 | --- 40 | apiVersion: v1 41 | kind: Service 42 | metadata: 43 | labels: 44 | app: {{ template "fullname" . }}-auth 45 | name: {{ template "fullname" . }}-auth 46 | spec: 47 | ports: 48 | - port: 3200 49 | targetPort: 3000 50 | selector: 51 | app: {{ template "fullname" . }}-auth 52 | --- 53 | apiVersion: v1 54 | kind: Service 55 | metadata: 56 | labels: 57 | app: {{ template "fullname" . }}-apk-storage 58 | name: {{ template "fullname" . }}-apk-storage 59 | spec: 60 | ports: 61 | - port: 3300 62 | targetPort: 3000 63 | selector: 64 | app: {{ template "fullname" . }}-apk-storage 65 | --- 66 | apiVersion: v1 67 | kind: Service 68 | metadata: 69 | labels: 70 | app: {{ template "fullname" . }}-img-storage 71 | name: {{ template "fullname" . }}-img-storage 72 | spec: 73 | ports: 74 | - port: 3400 75 | targetPort: 3000 76 | selector: 77 | app: {{ template "fullname" . }}-img-storage 78 | --- 79 | apiVersion: v1 80 | kind: Service 81 | metadata: 82 | labels: 83 | app: {{ template "fullname" . }}-storage 84 | name: {{ template "fullname" . }}-storage 85 | spec: 86 | ports: 87 | - port: 3500 88 | targetPort: 3000 89 | selector: 90 | app: {{ template "fullname" . }}-storage 91 | --- 92 | apiVersion: v1 93 | kind: Service 94 | metadata: 95 | labels: 96 | app: {{ template "fullname" . }}-triproxy-app 97 | name: {{ template "fullname" . }}-triproxy-app 98 | spec: 99 | ports: 100 | - port: 7150 101 | targetPort: 7150 102 | name: bind-pub 103 | - port: 7160 104 | targetPort: 7160 105 | name: bind-dealer 106 | - port: 7170 107 | targetPort: 7170 108 | name: bind-pull 109 | selector: 110 | app: {{ template "fullname" . }}-triproxy-app 111 | --- 112 | apiVersion: v1 113 | kind: Service 114 | metadata: 115 | labels: 116 | app: {{ template "fullname" . }}-triproxy-dev 117 | name: {{ template "fullname" . }}-triproxy-dev 118 | spec: 119 | ports: 120 | - port: 7250 121 | targetPort: 7250 122 | name: bind-pub 123 | - port: 7260 124 | targetPort: 7260 125 | name: bind-dealer 126 | - port: 7270 127 | targetPort: 7270 128 | name: bind-pull 129 | selector: 130 | app: {{ template "fullname" . }}-triproxy-dev 131 | --- 132 | apiVersion: v1 133 | kind: Service 134 | metadata: 135 | labels: 136 | app: {{ template "fullname" . }}-websocket 137 | name: {{ template "fullname" . }}-websocket 138 | spec: 139 | ports: 140 | - port: 3600 141 | targetPort: 3000 142 | selector: 143 | app: {{ template "fullname" . }}-websocket 144 | -------------------------------------------------------------------------------- /openstf/values.yaml: -------------------------------------------------------------------------------- 1 | stf: 2 | image: 3 | repository: openstf/stf 4 | tag: v3.2.0 5 | pullPolicy: IfNotPresent 6 | api: 7 | replicas: 3 8 | resources: 9 | limits: 10 | cpu: 500m 11 | memory: 512Mi 12 | app: 13 | replicas: 1 14 | resources: 15 | limits: 16 | cpu: 500m 17 | memory: 512Mi 18 | auth: 19 | replicas: 1 20 | resources: 21 | limits: 22 | cpu: 100m 23 | memory: 256Mi 24 | processor: 25 | replicas: 5 26 | resources: 27 | limits: 28 | cpu: 500m 29 | memory: 512Mi 30 | reaper: 31 | replicas: 1 32 | resources: 33 | limits: 34 | cpu: 500m 35 | memory: 512Mi 36 | apkStorage: 37 | replicas: 1 38 | resources: 39 | limits: 40 | cpu: 200m 41 | memory: 256Mi 42 | imgStorage: 43 | replicas: 1 44 | resources: 45 | limits: 46 | cpu: 100m 47 | memory: 256Mi 48 | storage: 49 | replicas: 1 50 | resources: 51 | limits: 52 | cpu: 100m 53 | memory: 256Mi 54 | triproxyDev: 55 | replicas: 1 56 | resources: 57 | limits: 58 | cpu: 500m 59 | memory: 512Mi 60 | triproxyApp: 61 | replicas: 1 62 | resources: 63 | limits: 64 | cpu: 500m 65 | memory: 512Mi 66 | websocket: 67 | replicas: 1 68 | resources: 69 | limits: 70 | cpu: 500m 71 | memory: 512Mi 72 | migration: 73 | resources: 74 | limits: 75 | cpu: 100m 76 | memory: 256Mi 77 | nginx: 78 | image: 79 | repository: nginx 80 | tag: 1.13.8-alpine 81 | pullPolicy: IfNotPresent 82 | replicas: 2 83 | resources: 84 | requests: 85 | cpu: 500m 86 | memory: 256Mi 87 | limits: 88 | cpu: 1 89 | memory: 512Mi 90 | adb: 91 | image: 92 | repository: agoda/adb-butler 93 | tag: latest 94 | pullPolicy: Always 95 | resources: 96 | requests: 97 | cpu: 300m 98 | memory: 256Mi 99 | limits: 100 | cpu: 1 101 | memory: 2Gi 102 | provider: 103 | real: 104 | tethering: 105 | enabled: false 106 | dns: 8.8.8.8 107 | resources: 108 | limits: 109 | cpu: 2 110 | memory: 4096Mi 111 | emulator: 112 | readinessProbe: 113 | initialDelaySeconds: 60 114 | periodSeconds: 30 115 | failureThreshold: 2 116 | livenessProbe: 117 | initialDelaySeconds: 10 118 | periodSeconds: 10 119 | resources: 120 | limits: 121 | cpu: 500m 122 | memory: 512Mi 123 | db: 124 | url: rethinkdb-rethinkdb-proxy.openstf 125 | port: 28015 126 | password: rethinkdb 127 | metrics: 128 | image: 129 | repository: malinskiy/cron-curl-jq-alpine 130 | tag: 1.0.1 131 | pullPolicy: IfNotPresent 132 | resources: 133 | limits: 134 | cpu: 500m 135 | memory: 256Mi 136 | # OpenSTF API token 137 | token: 138 | dns: 139 | resolver: coredns.kube-system 140 | # pullSecret: pullsecret 141 | ingress: 142 | enabled: false 143 | # By default applies proper annotation for nginx ingress 144 | annotations: 145 | hostname: openstf.local 146 | ssl: 147 | enabled: false 148 | secret: openstf.local-tls-secret 149 | androidDevicesTolerations: 150 | - effect: NoSchedule 151 | key: openstf.io/provider 152 | operator: Equal 153 | value: "true" 154 | emulators: 155 | imagePrefix: agoda/docker-emulator-android- 156 | imageVersion: 25 157 | pullPolicy: IfNotPresent 158 | resources: 159 | requests: 160 | cpu: 1 161 | limits: 162 | cpu: 2 163 | types: 164 | - name: phone 165 | args: "" 166 | note: ci/uiTest/phone 167 | timezone: "Etc/UTC" 168 | instances: 169 | - version: 26 170 | count: 1 171 | - version: 25 172 | count: 1 173 | - version: 24 174 | count: 1 175 | - version: 23 176 | count: 1 177 | - version: 22 178 | count: 1 179 | - version: 21 180 | count: 1 181 | - version: 19 182 | count: 1 183 | - version: 18 184 | count: 1 185 | - version: 17 186 | count: 1 187 | - version: 16 188 | count: 0 189 | - name: tablet7i 190 | note: ci/uiTest/tablet/7inch 191 | args: "skin.name=600x1024;hw.lcd.density=160;hw.lcd.height=600;hw.lcd.width=1024;hw.device.name=7in WSVGA (Tablet);avd.ini.displayname=7 WSVGA (Tablet) API 23;" 192 | timezone: "Etc/UTC" 193 | instances: 194 | - version: 25 195 | count: 1 196 | - name: tablet10i 197 | note: ci/uiTest/tablet/10inch 198 | args: "skin.name=800x1280;hw.lcd.density=160;hw.lcd.height=800;hw.lcd.width=1280;hw.device.name=10.1in WXGA (Tablet);avd.ini.displayname=10.1 WXGA (Tablet) API 23" 199 | timezone: "Etc/UTC" 200 | instances: 201 | - version: 25 202 | count: 1 203 | telegraf: 204 | image: 205 | repository: "telegraf" 206 | tag: "1.5-alpine" 207 | pullPolicy: IfNotPresent 208 | resources: 209 | requests: 210 | memory: 256Mi 211 | cpu: 0.1 212 | limits: 213 | memory: 2Gi 214 | cpu: 1 215 | config: 216 | global_tags: 217 | # cluster: "mycluster" 218 | agent: 219 | interval: "1m" 220 | round_interval: true 221 | metric_buffer_limit: 1000 222 | collection_jitter: "0s" 223 | flush_interval: "1m" 224 | flush_jitter: "0s" 225 | outputs: 226 | prometheus: 227 | enabled: true 228 | graphite: 229 | enabled: false 230 | # server: "graphite.server" 231 | # prefix: "android.devices" 232 | template: "host.tags.measurement.field" 233 | timeout: 5 234 | -------------------------------------------------------------------------------- /scripts/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /scripts/clean-offline-x86.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/node 2 | 3 | var r = require('rethinkdb') 4 | 5 | r.connect({ 6 | host: 'localhost', 7 | port: 28015, 8 | db: 'stf', 9 | authKey: 'rethinkdb' 10 | }, function(err, conn) { 11 | if (err) throw err; 12 | 13 | r.table('devices') 14 | .filter( 15 | { 16 | abi: 'x86', 17 | present: false 18 | } 19 | ) 20 | .delete() 21 | .run(conn, function(err, something) { 22 | if (err) throw err; 23 | 24 | conn.close(); 25 | console.log('cleaned devices: ' + something['deleted']); 26 | }); 27 | 28 | }); 29 | --------------------------------------------------------------------------------