github.com/abayer/test-infra@v0.0.5/prow/getting_started.md (about) 1 # How to turn up a new cluster 2 3 Prow should run anywhere that Kubernetes runs. Here are the steps required to 4 set up a basic prow cluster on [GKE](https://cloud.google.com/container-engine/). 5 Prow will work on any Kubernetes cluster, so feel free to turn up a cluster 6 some other way and skip the first step. You can set up a project on GCP using 7 the [cloud console](https://console.cloud.google.com/). 8 9 ## Create the cluster 10 11 I'm assuming that `PROJECT` and `ZONE` environment variables are set. 12 13 ```sh 14 export PROJECT=your-project 15 export ZONE=us-west1-a 16 ``` 17 18 Run the following to create the cluster. This will also set up `kubectl` to 19 point to the new cluster. 20 21 ```sh 22 gcloud container --project "${PROJECT}" clusters create prow \ 23 --zone "${ZONE}" --machine-type n1-standard-4 --num-nodes 2 24 ``` 25 26 ## Create cluster role bindings 27 As of 1.8 Kubernetes uses [Role-Based Access Control (“RBAC”)](https://kubernetes.io/docs/admin/authorization/rbac/) to drive authorization decisions, allowing `cluster-admin` to dynamically configure policies. 28 To create cluster resources you need to grant a user `cluster-admin` role in all namespaces for the cluster. 29 30 For Prow on GCP, you can use the following command. 31 ```sh 32 kubectl create clusterrolebinding cluster-admin-binding \ 33 --clusterrole cluster-admin --user $(gcloud config get-value account) 34 ``` 35 36 For Prow on other platforms, the following command will likely work. 37 ```sh 38 kubectl create clusterrolebinding cluster-admin-binding-"${USER}" \ 39 --clusterrole=cluster-admin --user="${USER}" 40 ``` 41 On some platforms the `USER` variable may not map correctly to the user 42 in-cluster. If you see an error of the following form, this is likely the case. 43 44 ```console 45 Error from server (Forbidden): error when creating 46 "prow/cluster/starter.yaml": roles.rbac.authorization.k8s.io "<account>" is 47 forbidden: attempt to grant extra privileges: 48 [PolicyRule{Resources:["pods/log"], APIGroups:[""], Verbs:["get"]} 49 PolicyRule{Resources:["prowjobs"], APIGroups:["prow.k8s.io"], Verbs:["get"]} 50 APIGroups:["prow.k8s.io"], Verbs:["list"]}] user=&{<CLUSTER_USER> 51 [system:authenticated] map[]}... 52 ``` 53 54 Run the previous command substituting `USER` with `CLUSTER_USER` from the error 55 message above to solve this issue. 56 ```sh 57 kubectl create clusterrolebinding cluster-admin-binding-"<CLUSTER_USER>" \ 58 --clusterrole=cluster-admin --user="<CLUSTER_USER>" 59 ``` 60 61 There are [relevant docs on Kubernetes Authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authentication-strategies) that may help if neither of the above work. 62 63 64 65 ## Create the GitHub secrets 66 67 You will need two secrets to talk to GitHub. The `hmac-token` is the token that 68 you give to GitHub for validating webhooks. Generate it using any reasonable 69 randomness-generator, eg `openssl rand -hex 20`. The `oauth-token` is an OAuth2 token 70 that has read and write access to the bot account. Generate it from the 71 [account's settings -> Personal access tokens -> Generate new token][1]. 72 73 ```sh 74 kubectl create secret generic hmac-token --from-file=hmac=/path/to/hook/secret 75 kubectl create secret generic oauth-token --from-file=oauth=/path/to/oauth/secret 76 ``` 77 78 #### Bot account 79 80 The bot account used by prow must be granted owner level access to the Github 81 orgs that prow will operate on. Note that events triggered by this account are 82 ignored by some prow plugins. It is prudent to use a different bot account for 83 other Github automation that prow should interact with to prevent events from 84 being ignored unjustly. 85 86 ## Run the prow components in the cluster 87 88 Run the following command to start up a basic set of prow components. 89 90 ```sh 91 kubectl apply -f cluster/starter.yaml 92 ``` 93 94 After a moment, the cluster components will be running. 95 96 ```console 97 $ kubectl get deployments 98 NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 99 deck 2 2 2 2 1m 100 hook 2 2 2 2 1m 101 horologium 1 1 1 1 1m 102 plank 1 1 1 1 1m 103 sinker 1 1 1 1 1m 104 ``` 105 106 Find out your external address. It might take a couple minutes for the IP to 107 show up. 108 109 ```console 110 $ kubectl get ingress ing 111 NAME HOSTS ADDRESS PORTS AGE 112 ing * an.ip.addr.ess 80 3m 113 ``` 114 115 Go to that address in a web browser and verify that the "echo-test" job has a 116 green check-mark next to it. At this point you have a prow cluster that is ready 117 to start receiving GitHub events! 118 119 ## Add the webhook to GitHub 120 121 On the GitHub repo you would like to use, go to Settings -> Webhooks -> Add 122 webhook. You can also add org-level webhooks. 123 124 Set the payload URL to `http://<IP-FROM-INGRESS>/hook`, the content type to 125 `application/json`, the secret to your HMAC secret, and ask it to send everything. 126 After you've created your webhook, GitHub will indicate that it successfully 127 sent an event by putting a green checkmark under "Recent Deliveries." 128 129 # Next steps 130 131 ## Enable some plugins by modifying `plugins.yaml` 132 133 Create a file called `plugins.yaml` and add the following to it: 134 135 ```yaml 136 plugins: 137 YOUR_ORG/YOUR_REPO: 138 - size 139 ``` 140 141 Replace `YOUR_ORG/YOUR_REPO:` with the appropriate values. If you want, you can 142 instead just say `YOUR_ORG:` and the plugin will run for every repo in the org. 143 144 Run the following to test the file, replacing the path as necessary: 145 146 ```sh 147 bazel run //prow/cmd/config -- --plugin-config=path/to/plugins.yaml 148 ``` 149 150 There should be no errors. You can run this as a part of your presubmit testing 151 so that any errors are caught before you try to update. 152 153 Now run the following to update the configmap, replacing the path as necessary: 154 155 ```sh 156 kubectl create configmap plugins \ 157 --from-file=plugins.yaml=path/to/plugins.yaml --dry-run -o yaml \ 158 | kubectl replace configmap plugins -f - 159 ``` 160 161 We added a make rule to do this for us: 162 163 ```Make 164 get-cluster-credentials: 165 gcloud container clusters get-credentials "$(CLUSTER)" --project="$(PROJECT)" --zone="$(ZONE)" 166 167 update-plugins: get-cluster-credentials 168 kubectl create configmap plugins --from-file=plugins.yaml=plugins.yaml --dry-run -o yaml | kubectl replace configmap plugins -f - 169 ``` 170 171 Now when you open a PR, it will automatically be labelled with a `size/*` 172 label. When you make a change to the plugin config and push it with `make 173 update-plugins`, you do not need to redeploy any of your cluster components. 174 They will pick up the change within a few minutes. 175 176 ## Add more jobs by modifying `config.yaml` 177 178 Create a file called `config.yaml`, and add the following to it: 179 180 ```yaml 181 periodics: 182 - interval: 10m 183 agent: kubernetes 184 name: echo-test 185 spec: 186 containers: 187 - image: alpine 188 command: ["/bin/date"] 189 postsubmits: 190 YOUR_ORG/YOUR_REPO: 191 - name: test-postsubmit 192 agent: kubernetes 193 spec: 194 containers: 195 - image: alpine 196 command: ["/bin/printenv"] 197 presubmits: 198 YOUR_ORG/YOUR_REPO: 199 - name: test-presubmit 200 trigger: "(?m)^/test this" 201 rerun_command: "/test this" 202 context: test-presubmit 203 always_run: true 204 skip_report: true 205 agent: kubernetes 206 spec: 207 containers: 208 - image: alpine 209 command: ["/bin/printenv"] 210 ``` 211 212 Run the following to test the file, replacing the path as necessary: 213 214 ```sh 215 bazel run //prow/cmd/config -- --config-path=path/to/config.yaml 216 ``` 217 218 Now run the following to update the configmap. 219 220 ```sh 221 kubectl create configmap config \ 222 --from-file=config.yaml=path/to/config.yaml --dry-run -o yaml | kubectl replace configmap config -f - 223 ``` 224 225 We use a make rule: 226 227 ```Make 228 update-config: get-cluster-credentials 229 kubectl create configmap config --from-file=config.yaml=config.yaml --dry-run -o yaml | kubectl replace configmap config -f - 230 ``` 231 232 Presubmits and postsubmits are triggered by the `trigger` plugin. Be sure to 233 enable that plugin by adding it to the list you created in the last section. 234 235 Now when you open a PR it will automatically run the presubmit that you added 236 to this file. You can see it on your prow dashboard. Once you are happy that it 237 is stable, switch `skip_report` to `false`. Then, it will post a status on the 238 PR. When you make a change to the config and push it with `make update-config`, 239 you do not need to redeploy any of your cluster components. They will pick up 240 the change within a few minutes. 241 242 When you push a new change, the postsubmit job will run. 243 244 For more information on the job environment, see [How to add new jobs][2]. 245 246 ## Run test pods in a different namespace 247 248 You may choose to keep prowjobs or run tests in a different namespace. First 249 create the namespace by `kubectl create -f`ing this: 250 251 ```yaml 252 apiVersion: v1 253 kind: Namespace 254 metadata: 255 name: prow 256 ``` 257 258 Now, in `config.yaml`, set `prowjob_namespace` or `pod_namespace` to the 259 name from the YAML file. You can then use RBAC roles to limit what test pods 260 can do. 261 262 263 ## Run test pods in different clusters 264 265 You may choose to run test pods in a separate cluster entirely. Create a secret 266 containing a `{"cluster-name": {cluster-details}}` map like this: 267 268 ```yaml 269 default: 270 endpoint: https://<master-ip> 271 clientCertificate: <base64-encoded cert> 272 clientKey: <base64-encoded key> 273 clusterCaCertificate: <base64-encoded cert> 274 other: 275 endpoint: https://<master-ip> 276 clientCertificate: <base64-encoded cert> 277 clientKey: <base64-encoded key> 278 clusterCaCertificate: <base64-encoded cert> 279 ``` 280 281 Use [mkbuild-cluster][5] to determine these values: 282 ```sh 283 bazel run //prow/cmd/mkbuild-cluster -- \ 284 --project=P --zone=Z --cluster=C \ 285 --alias=A \ 286 --print-entry | tee cluster.yaml 287 kubectl create secret generic build-cluster --from-file=cluster.yaml 288 ``` 289 290 Mount this secret into the prow components that need it and set 291 the `--build-cluster` flag to the location you mount it at. For instance, you 292 will need to merge the following into the plank deployment: 293 294 ```yaml 295 spec: 296 containers: 297 - name: plank 298 args: 299 - --build-cluster=/etc/foo/cluster.yaml # basename matches --from-file key 300 volumeMounts: 301 - mountPath: /etc/foo 302 name: cluster 303 readOnly: true 304 volumes: 305 - name: cluster 306 secret: 307 defaultMode: 420 308 secretName: build-cluster # example above contains a cluster.yaml key 309 ``` 310 311 Configure jobs to use the non-default cluster with the `cluster:` field. 312 The above example `cluster.yaml` defines two clusters: `default` and `other` to schedule jobs, which we can use as follows: 313 ```yaml 314 periodics: 315 - name: cluster-unspecified 316 # cluster: 317 interval: 10m 318 agent: kubernetes 319 spec: 320 containers: 321 - image: alpine 322 command: ["/bin/date"] 323 - name: cluster-default 324 cluster: default 325 interval: 10m 326 agent: kubernetes 327 spec: 328 containers: 329 - image: alpine 330 command: ["/bin/date"] 331 - name: cluster-other 332 cluster: other 333 interval: 10m 334 agent: kubernetes 335 spec: 336 containers: 337 - image: alpine 338 command: ["/bin/date"] 339 ``` 340 341 This results in: 342 * The `cluster-unspecified` and `default-cluster` jobs run in the `default` cluster. 343 * The `cluster-other` job runs in the `other` cluster. 344 345 See [mkbuild-cluster][5] for more details about how to create/update `cluster.yaml`. 346 347 ## Configure SSL 348 349 Use [kube-lego][3] for automatic LetsEncrypt integration. If you 350 already have a cert then follow the [official docs][4] to set up HTTPS 351 termination. Promote your ingress IP to static IP. On GKE, run: 352 353 ```sh 354 gcloud compute addresses create [ADDRESS_NAME] --addresses [IP_ADDRESS] --region [REGION] 355 ``` 356 357 Point the DNS record for your domain to point at that ingress IP. The convention 358 for naming is `prow.org.io`, but of course that's not a requirement. 359 360 Then, install kube-lego as described in its readme. You don't need to run it in 361 a separate namespace. 362 363 [1]: https://github.com/settings/tokens 364 [2]: ./README.md##how-to-add-new-jobs 365 [3]: https://github.com/jetstack/kube-lego 366 [4]: https://kubernetes.io/docs/concepts/services-networking/ingress/#tls 367 [5]: /prow/cmd/mkbuild-cluster/