From 11b9391e900e325b5143951178c824fbafc219da Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 9 Nov 2018 15:43:57 +0100 Subject: [PATCH 01/10] Initial agentless SaltStack config for generic-worker on macOS --- etc/taskcluster/macos/.gitignore | 1 + etc/taskcluster/macos/README.md | 16 +++++++++++++ etc/taskcluster/macos/Saltfile | 2 ++ etc/taskcluster/macos/config/master | 4 ++++ etc/taskcluster/macos/config/roster | 2 ++ etc/taskcluster/macos/salt-ssh | 14 +++++++++++ .../macos/states/generic-worker.sls | 23 +++++++++++++++++++ etc/taskcluster/macos/states/top.sls | 3 +++ 8 files changed, 65 insertions(+) create mode 100644 etc/taskcluster/macos/.gitignore create mode 100644 etc/taskcluster/macos/README.md create mode 100644 etc/taskcluster/macos/Saltfile create mode 100644 etc/taskcluster/macos/config/master create mode 100644 etc/taskcluster/macos/config/roster create mode 100755 etc/taskcluster/macos/salt-ssh create mode 100644 etc/taskcluster/macos/states/generic-worker.sls create mode 100644 etc/taskcluster/macos/states/top.sls diff --git a/etc/taskcluster/macos/.gitignore b/etc/taskcluster/macos/.gitignore new file mode 100644 index 00000000000..96286f5841e --- /dev/null +++ b/etc/taskcluster/macos/.gitignore @@ -0,0 +1 @@ +.salt diff --git a/etc/taskcluster/macos/README.md b/etc/taskcluster/macos/README.md new file mode 100644 index 00000000000..1ca96baa10b --- /dev/null +++ b/etc/taskcluster/macos/README.md @@ -0,0 +1,16 @@ +# macOS + +Servo’s macOS workers for Taskcluster are configured with +SaltStack in [agentless] mode. + +[agentless]: https://docs.saltstack.com/en/getstarted/ssh/index.html + +Either run `./salt-ssh` +to automatically install `salt-ssh` in `mach`’s existing Python virtualenv, +or install `salt-ssh` through some other mean and run in from this directory. + +```sh +cd etc/taskcluster/macos +./salt-ssh '*' test.ping +./salt-ssh '*' state.apply test=True +``` \ No newline at end of file diff --git a/etc/taskcluster/macos/Saltfile b/etc/taskcluster/macos/Saltfile new file mode 100644 index 00000000000..044cd70e61e --- /dev/null +++ b/etc/taskcluster/macos/Saltfile @@ -0,0 +1,2 @@ +salt-ssh: + config_dir: ./config \ No newline at end of file diff --git a/etc/taskcluster/macos/config/master b/etc/taskcluster/macos/config/master new file mode 100644 index 00000000000..cf59c94f2a9 --- /dev/null +++ b/etc/taskcluster/macos/config/master @@ -0,0 +1,4 @@ +root_dir: .salt +file_roots: + base: + - states \ No newline at end of file diff --git a/etc/taskcluster/macos/config/roster b/etc/taskcluster/macos/config/roster new file mode 100644 index 00000000000..ddfe7089497 --- /dev/null +++ b/etc/taskcluster/macos/config/roster @@ -0,0 +1,2 @@ +mac1: + host: servo-tc-mac1.servo.org \ No newline at end of file diff --git a/etc/taskcluster/macos/salt-ssh b/etc/taskcluster/macos/salt-ssh new file mode 100755 index 00000000000..5a6439a18e2 --- /dev/null +++ b/etc/taskcluster/macos/salt-ssh @@ -0,0 +1,14 @@ +#!/bin/sh + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +set -o errexit +set -o nounset +set -o pipefail + +cd "$(dirname $0)" +VENV_BIN="../../../python/_virtualenv/bin" +[ -x "${VENV_BIN}/salt-ssh" ] || "${VENV_BIN}/pip" install salt-ssh +"${VENV_BIN}/salt-ssh" "${@}" \ No newline at end of file diff --git a/etc/taskcluster/macos/states/generic-worker.sls b/etc/taskcluster/macos/states/generic-worker.sls new file mode 100644 index 00000000000..e07c7fbd5bc --- /dev/null +++ b/etc/taskcluster/macos/states/generic-worker.sls @@ -0,0 +1,23 @@ +/usr/local/bin/generic-worker: + file.managed: + - name: + - source: https://github.com/taskcluster/generic-worker/releases/download/v11.0.1/generic-worker-darwin-amd64 + - source_hash: sha256=059331865670d3722a710f0b6f4dae97d347811cc347d1810c6dfc1b413c4b48 + - mode: 755 + - makedirs: True + +/usr/local/bin/livelog: + file.managed: + - source: https://github.com/taskcluster/livelog/releases/download/v1.1.0/livelog-darwin-amd64 + - source_hash: sha256=be5d4b998b208afd802ac6ce6c4d4bbf0fb3816bb039a300626abbc999dfe163 + - mode: 755 + - makedirs: True + +/etc/generic-worker: + file.directory: + - dir_mode: 700 + +generic-worker new-openpgp-keypair --file /etc/generic-worker/key: + cmd.run: + - creates: /etc/generic-worker/key + - prepend_path: /usr/local/bin \ No newline at end of file diff --git a/etc/taskcluster/macos/states/top.sls b/etc/taskcluster/macos/states/top.sls new file mode 100644 index 00000000000..79f7a40eed3 --- /dev/null +++ b/etc/taskcluster/macos/states/top.sls @@ -0,0 +1,3 @@ +base: + 'mac*': + - generic-worker \ No newline at end of file From 889d4799352206af9a71787c69ee04c15c17855e Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 9 Nov 2018 17:08:47 +0100 Subject: [PATCH 02/10] Initial generic-worker config file --- etc/taskcluster/macos/README.md | 6 +++- etc/taskcluster/macos/Saltfile | 3 +- .../macos/states/generic-worker.sls | 29 ++++++++++++++----- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/etc/taskcluster/macos/README.md b/etc/taskcluster/macos/README.md index 1ca96baa10b..54252cc2f9e 100644 --- a/etc/taskcluster/macos/README.md +++ b/etc/taskcluster/macos/README.md @@ -13,4 +13,8 @@ or install `salt-ssh` through some other mean and run in from this directory. cd etc/taskcluster/macos ./salt-ssh '*' test.ping ./salt-ssh '*' state.apply test=True -``` \ No newline at end of file +``` + +## Worker’s client ID + +`project/servo/worker/macos/1` \ No newline at end of file diff --git a/etc/taskcluster/macos/Saltfile b/etc/taskcluster/macos/Saltfile index 044cd70e61e..ee77404e3dc 100644 --- a/etc/taskcluster/macos/Saltfile +++ b/etc/taskcluster/macos/Saltfile @@ -1,2 +1,3 @@ salt-ssh: - config_dir: ./config \ No newline at end of file + config_dir: ./config + state_verbose: False \ No newline at end of file diff --git a/etc/taskcluster/macos/states/generic-worker.sls b/etc/taskcluster/macos/states/generic-worker.sls index e07c7fbd5bc..a832d949596 100644 --- a/etc/taskcluster/macos/states/generic-worker.sls +++ b/etc/taskcluster/macos/states/generic-worker.sls @@ -1,4 +1,7 @@ -/usr/local/bin/generic-worker: +{% set bin = "/usr/local/bin" %} +{% set keyfile = "/etc/generic-worker/key" %} + +{{ bin }}/generic-worker: file.managed: - name: - source: https://github.com/taskcluster/generic-worker/releases/download/v11.0.1/generic-worker-darwin-amd64 @@ -6,18 +9,28 @@ - mode: 755 - makedirs: True -/usr/local/bin/livelog: +{{ bin }}/livelog: file.managed: - source: https://github.com/taskcluster/livelog/releases/download/v1.1.0/livelog-darwin-amd64 - source_hash: sha256=be5d4b998b208afd802ac6ce6c4d4bbf0fb3816bb039a300626abbc999dfe163 - mode: 755 - makedirs: True -/etc/generic-worker: - file.directory: - - dir_mode: 700 +/etc/generic-worker/config.json: + file.serialize: + - makedirs: True + - mode: 600 + - show_changes: False + - formatter: json + - dataset: + provisionerId: proj-servo + workerType: macos + workerId: servo-macos-1 + clientId: project/servo/worker/macos/1 + publicIP: {{ salt.network.ip_addrs()[0] }} + signingKeyLocation: {{ keyfile }} -generic-worker new-openpgp-keypair --file /etc/generic-worker/key: +generic-worker new-openpgp-keypair --file {{ keyfile }}: cmd.run: - - creates: /etc/generic-worker/key - - prepend_path: /usr/local/bin \ No newline at end of file + - creates: {{ keyfile }} + - prepend_path: {{ bin }} From 70f507879ff00aa7d649af344be98a628b1de5af Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 12 Nov 2018 19:48:17 +0100 Subject: [PATCH 03/10] generic-worker on macOS: run as unprivileged user, start as a service --- etc/taskcluster/macos/README.md | 25 ++++++- etc/taskcluster/macos/Saltfile | 2 +- etc/taskcluster/macos/config/master | 5 +- etc/taskcluster/macos/config/roster | 7 +- .../modules/pillar/taskcluster_secrets.py | 16 +++++ etc/taskcluster/macos/salt-ssh | 2 +- .../macos/states/generic-worker.sls | 72 ++++++++++++++++--- etc/taskcluster/packet.net/README.md | 2 +- 8 files changed, 115 insertions(+), 16 deletions(-) create mode 100644 etc/taskcluster/macos/modules/pillar/taskcluster_secrets.py diff --git a/etc/taskcluster/macos/README.md b/etc/taskcluster/macos/README.md index 54252cc2f9e..0e5b1633701 100644 --- a/etc/taskcluster/macos/README.md +++ b/etc/taskcluster/macos/README.md @@ -1,7 +1,7 @@ # macOS -Servo’s macOS workers for Taskcluster are configured with -SaltStack in [agentless] mode. +This is the configuration for the `proj-servo/macos` worker type. +These macOS workers are configured with SaltStack in [agentless] mode. [agentless]: https://docs.saltstack.com/en/getstarted/ssh/index.html @@ -15,6 +15,25 @@ cd etc/taskcluster/macos ./salt-ssh '*' state.apply test=True ``` +## Servers + +Servers are provisioned manually from MacStadium. +The `config/roster` file lists them by DNS name. + + +## Taskcluster secrets + +This SaltStack configuration has a custom module that uses Taskcluster’s +[secrets service](https://tools.taskcluster.net/secrets/). +These secrets include an [authentication token]( +You’ll need to authenticate with a Taskcluster client ID +that has scope `secrets:get:project/servo/*`. +This should be the case if you’re a Servo project administrator (the `project-admin:servo` role). + + ## Worker’s client ID -`project/servo/worker/macos/1` \ No newline at end of file +Workers are configured to authenticate with client ID +[`project/servo/worker/macos/1`]( +https://tools.taskcluster.net/auth/clients/project%2Fservo%2Fworker%macos%2F1). +This client has the scopes required to run tasks for this worker type. \ No newline at end of file diff --git a/etc/taskcluster/macos/Saltfile b/etc/taskcluster/macos/Saltfile index ee77404e3dc..7298fc32ef7 100644 --- a/etc/taskcluster/macos/Saltfile +++ b/etc/taskcluster/macos/Saltfile @@ -1,3 +1,3 @@ salt-ssh: config_dir: ./config - state_verbose: False \ No newline at end of file + state_verbose: False diff --git a/etc/taskcluster/macos/config/master b/etc/taskcluster/macos/config/master index cf59c94f2a9..50c5179d539 100644 --- a/etc/taskcluster/macos/config/master +++ b/etc/taskcluster/macos/config/master @@ -1,4 +1,7 @@ root_dir: .salt file_roots: base: - - states \ No newline at end of file + - states +extension_modules: ../modules +ext_pillar: + - taskcluster_secrets: diff --git a/etc/taskcluster/macos/config/roster b/etc/taskcluster/macos/config/roster index ddfe7089497..ed11c2b8530 100644 --- a/etc/taskcluster/macos/config/roster +++ b/etc/taskcluster/macos/config/roster @@ -1,2 +1,7 @@ mac1: - host: servo-tc-mac1.servo.org \ No newline at end of file + host: servo-tc-mac1.servo.org + + # https://github.com/saltstack/salt/issues/50477 + minion_opts: + providers: + user: mac_user diff --git a/etc/taskcluster/macos/modules/pillar/taskcluster_secrets.py b/etc/taskcluster/macos/modules/pillar/taskcluster_secrets.py new file mode 100644 index 00000000000..49229f73a4d --- /dev/null +++ b/etc/taskcluster/macos/modules/pillar/taskcluster_secrets.py @@ -0,0 +1,16 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import os +import sys +sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "..", "packet.net")) +import tc + + +def ext_pillar(_minion_id, _pillar, *_args): + tc.check() + data = {} + data.update(tc.secret("project/servo/tc-client/worker/macos/1")) + data.update(tc.secret("project/servo/livelog-secret/1")) + return data diff --git a/etc/taskcluster/macos/salt-ssh b/etc/taskcluster/macos/salt-ssh index 5a6439a18e2..b68cdf92c7f 100755 --- a/etc/taskcluster/macos/salt-ssh +++ b/etc/taskcluster/macos/salt-ssh @@ -11,4 +11,4 @@ set -o pipefail cd "$(dirname $0)" VENV_BIN="../../../python/_virtualenv/bin" [ -x "${VENV_BIN}/salt-ssh" ] || "${VENV_BIN}/pip" install salt-ssh -"${VENV_BIN}/salt-ssh" "${@}" \ No newline at end of file +"${VENV_BIN}/salt-ssh" "${@}" diff --git a/etc/taskcluster/macos/states/generic-worker.sls b/etc/taskcluster/macos/states/generic-worker.sls index a832d949596..c7d628dc1d8 100644 --- a/etc/taskcluster/macos/states/generic-worker.sls +++ b/etc/taskcluster/macos/states/generic-worker.sls @@ -1,5 +1,6 @@ {% set bin = "/usr/local/bin" %} -{% set keyfile = "/etc/generic-worker/key" %} +{% set user = "worker" %} +{% set home = "/Users/" + user %} {{ bin }}/generic-worker: file.managed: @@ -16,21 +17,76 @@ - mode: 755 - makedirs: True -/etc/generic-worker/config.json: +{{ user }}: + user.present: + - home: {{ home }} + +# `user.present`’s `createhome` is apparently not supported on macOS +{{ home }}: + file.directory: + - user: {{ user }} + +{{ home }}/config.json: file.serialize: - makedirs: True + - user: {{ user }} - mode: 600 - show_changes: False - formatter: json - dataset: provisionerId: proj-servo workerType: macos - workerId: servo-macos-1 - clientId: project/servo/worker/macos/1 + workerGroup: servo-macos + workerId: mac1 + tasksDir: {{ home }}/tasks publicIP: {{ salt.network.ip_addrs()[0] }} - signingKeyLocation: {{ keyfile }} + signingKeyLocation: {{ home }}/key + clientId: {{ pillar["client_id"] }} + accessToken: {{ pillar["access_token"] }} + livelogSecret: {{ pillar["livelog_secret"] }} -generic-worker new-openpgp-keypair --file {{ keyfile }}: +{{ bin }}/generic-worker new-openpgp-keypair --file {{ home }}/key: cmd.run: - - creates: {{ keyfile }} - - prepend_path: {{ bin }} + - creates: {{ home }}/key + - runas: worker + +/Library/LaunchAgents/net.generic.worker.plist: + file.managed: + - mode: 644 + - template: jinja + - contents: >- + + + + + Label + net.generic.worker + + ProgramArguments + + {{ bin }}/generic-worker + run + --config + config.json + + + KeepAlive + + + WorkingDirectory + {{ home }} + + UserName + {{ user }} + + StandardOutPath + stdout.log + + StandardErrorPath + stderr.log + + + +net.generic.worker: + service.running: + - enable: True diff --git a/etc/taskcluster/packet.net/README.md b/etc/taskcluster/packet.net/README.md index edbb5897833..408a28ba329 100644 --- a/etc/taskcluster/packet.net/README.md +++ b/etc/taskcluster/packet.net/README.md @@ -39,7 +39,7 @@ This should be the case if you’re a Servo project administrator (the `project- ## Worker’s client ID Workers are configured to authenticate with client ID -[project/servo/worker/docker-worker-kvm/1]( +[`project/servo/worker/docker-worker-kvm/1`]( https://tools.taskcluster.net/auth/clients/project%2Fservo%2Fworker%2Fdocker-worker-kvm%2F1). This client has the scopes required to run docker-worker as well as for tasks that we run on this worker type. \ No newline at end of file From a5089885acda9f67e956c08e126715693fcc0715 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 12 Nov 2018 21:23:30 +0100 Subject: [PATCH 04/10] generic-worker on macOS: restart service on config change --- .../macos/states/generic-worker.sls | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/etc/taskcluster/macos/states/generic-worker.sls b/etc/taskcluster/macos/states/generic-worker.sls index c7d628dc1d8..c10e0c54002 100644 --- a/etc/taskcluster/macos/states/generic-worker.sls +++ b/etc/taskcluster/macos/states/generic-worker.sls @@ -44,12 +44,27 @@ clientId: {{ pillar["client_id"] }} accessToken: {{ pillar["access_token"] }} livelogSecret: {{ pillar["livelog_secret"] }} + - watch_in: + - service: net.generic.worker {{ bin }}/generic-worker new-openpgp-keypair --file {{ home }}/key: cmd.run: - creates: {{ home }}/key - runas: worker +{{ home }}/run: + file.managed: + - mode: 744 + - user: {{ user }} + - template: jinja + - contents: |- + #!/bin/sh + # generic-worker overwrites its config file to fill in defaults, + # but we want to avoid touching config.json here + # so that SaltStack knows to (only) restart the service when it (really) changes. + cp -a config.json config-run.json + exec {{ bin }}/generic-worker run --config config-run.json + /Library/LaunchAgents/net.generic.worker.plist: file.managed: - mode: 644 @@ -64,10 +79,7 @@ ProgramArguments - {{ bin }}/generic-worker - run - --config - config.json + {{ home }}/run KeepAlive From ff1e2c2394cda3ce744e25e5629d52db8baa0d6a Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 12 Nov 2018 22:34:33 +0100 Subject: [PATCH 05/10] generic-worker on macOS: configure livelog --- .../macos/modules/pillar/taskcluster_secrets.py | 2 +- etc/taskcluster/macos/states/generic-worker.sls | 16 +++++++++++++++- etc/taskcluster/packet.net/tc.py | 13 +++++++++++++ .../packet.net/terraform_with_vars.py | 13 +++---------- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/etc/taskcluster/macos/modules/pillar/taskcluster_secrets.py b/etc/taskcluster/macos/modules/pillar/taskcluster_secrets.py index 49229f73a4d..7283f6ec088 100644 --- a/etc/taskcluster/macos/modules/pillar/taskcluster_secrets.py +++ b/etc/taskcluster/macos/modules/pillar/taskcluster_secrets.py @@ -12,5 +12,5 @@ def ext_pillar(_minion_id, _pillar, *_args): tc.check() data = {} data.update(tc.secret("project/servo/tc-client/worker/macos/1")) - data.update(tc.secret("project/servo/livelog-secret/1")) + data.update(tc.livelog()) return data diff --git a/etc/taskcluster/macos/states/generic-worker.sls b/etc/taskcluster/macos/states/generic-worker.sls index c10e0c54002..aeeb35f6e43 100644 --- a/etc/taskcluster/macos/states/generic-worker.sls +++ b/etc/taskcluster/macos/states/generic-worker.sls @@ -28,7 +28,6 @@ {{ home }}/config.json: file.serialize: - - makedirs: True - user: {{ user }} - mode: 600 - show_changes: False @@ -43,10 +42,25 @@ signingKeyLocation: {{ home }}/key clientId: {{ pillar["client_id"] }} accessToken: {{ pillar["access_token"] }} + livelogExecutable: {{ bin }}/livelog + livelogCertificate: {{ home }}/livelog.crt + livelogKey: {{ home }}/livelog.key livelogSecret: {{ pillar["livelog_secret"] }} - watch_in: - service: net.generic.worker +{{ home }}/livelog.crt: + file.managed: + - contents_pillar: livelog_cert + - user: {{ user }} + - mode: 600 + +{{ home }}/livelog.key: + file.managed: + - contents_pillar: livelog_key + - user: {{ user }} + - mode: 600 + {{ bin }}/generic-worker new-openpgp-keypair --file {{ home }}/key: cmd.run: - creates: {{ home }}/key diff --git a/etc/taskcluster/packet.net/tc.py b/etc/taskcluster/packet.net/tc.py index 63b07186f97..464d248682d 100644 --- a/etc/taskcluster/packet.net/tc.py +++ b/etc/taskcluster/packet.net/tc.py @@ -5,6 +5,7 @@ import os import sys import json +import base64 import subprocess @@ -20,6 +21,18 @@ def check(): "eval `taskcluster signin`\n") +def livelog(): + win2016 = api("awsProvisioner", "workerType", "servo-win2016") + files = win2016["secrets"]["files"] + assert all(f["encoding"] == "base64" for f in files) + files = {f.get("description"): f["content"] for f in files} + return { + "livelog_cert": base64.b64decode(files["SSL certificate for livelog"]), + "livelog_key": base64.b64decode(files["SSL key for livelog"]), + "livelog_secret": win2016["secrets"]["generic-worker"]["config"]["livelogSecret"], + } + + def packet_auth_token(): return secret("project/servo/packet.net-api-key")["key"] diff --git a/etc/taskcluster/packet.net/terraform_with_vars.py b/etc/taskcluster/packet.net/terraform_with_vars.py index 074f5c5aa00..891674838d9 100755 --- a/etc/taskcluster/packet.net/terraform_with_vars.py +++ b/etc/taskcluster/packet.net/terraform_with_vars.py @@ -6,7 +6,6 @@ import os import sys -import base64 import subprocess import tc @@ -16,13 +15,7 @@ def main(*args): tc.check() ssh_key = tc.secret("project/servo/ssh-keys/docker-worker-kvm") tc_creds = tc.secret("project/servo/tc-client/worker/docker-worker-kvm/1") - win2016 = tc.api("awsProvisioner", "workerType", "servo-win2016") - files_by_desc = {f.get("description"): f for f in win2016["secrets"]["files"]} - - def decode(description): - f = files_by_desc[description] - assert f["encoding"] == "base64" - return base64.b64decode(f["content"]) + livelog = tc.livelog() terraform_vars = dict( ssh_pub_key=ssh_key["public"], @@ -30,8 +23,8 @@ def main(*args): taskcluster_client_id=tc_creds["client_id"], taskcluster_access_token=tc_creds["access_token"], packet_api_key=tc.packet_auth_token(), - ssl_certificate=decode("SSL certificate for livelog"), - cert_key=decode("SSL key for livelog"), + ssl_certificate=livelog["livelog_cert_base64"], + cert_key=livelog["livelog_key_base64"], ) env = dict(os.environ) env["PACKET_AUTH_TOKEN"] = terraform_vars["packet_api_key"] From fe0e1ae7d38e097177cd02fe320b88f403facf13 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 12 Nov 2018 23:20:33 +0100 Subject: [PATCH 06/10] generic-worker on macOS: read-only config --- etc/taskcluster/macos/config/roster | 1 + .../macos/states/generic-worker.sls | 49 +++++++++---------- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/etc/taskcluster/macos/config/roster b/etc/taskcluster/macos/config/roster index ed11c2b8530..c1f1e5f6ee7 100644 --- a/etc/taskcluster/macos/config/roster +++ b/etc/taskcluster/macos/config/roster @@ -5,3 +5,4 @@ mac1: minion_opts: providers: user: mac_user + group: mac_group diff --git a/etc/taskcluster/macos/states/generic-worker.sls b/etc/taskcluster/macos/states/generic-worker.sls index aeeb35f6e43..3bacbabe204 100644 --- a/etc/taskcluster/macos/states/generic-worker.sls +++ b/etc/taskcluster/macos/states/generic-worker.sls @@ -1,4 +1,5 @@ {% set bin = "/usr/local/bin" %} +{% set etc = "/etc/generic-worker" %} {% set user = "worker" %} {% set home = "/Users/" + user %} @@ -17,19 +18,25 @@ - mode: 755 - makedirs: True +{{ user }} group: + group.present: + - name: {{ user }} + {{ user }}: user.present: - home: {{ home }} + - gid_from_name: True # `user.present`’s `createhome` is apparently not supported on macOS {{ home }}: file.directory: - user: {{ user }} -{{ home }}/config.json: +{{ etc }}/config.json: file.serialize: - - user: {{ user }} - - mode: 600 + - makedirs: True + - group: {{ user }} + - mode: 640 - show_changes: False - formatter: json - dataset: @@ -43,41 +50,28 @@ clientId: {{ pillar["client_id"] }} accessToken: {{ pillar["access_token"] }} livelogExecutable: {{ bin }}/livelog - livelogCertificate: {{ home }}/livelog.crt - livelogKey: {{ home }}/livelog.key + livelogCertificate: {{ etc }}/livelog.crt + livelogKey: {{ etc }}/livelog.key livelogSecret: {{ pillar["livelog_secret"] }} - watch_in: - service: net.generic.worker -{{ home }}/livelog.crt: +{{ etc }}/livelog.crt: file.managed: - contents_pillar: livelog_cert - - user: {{ user }} - - mode: 600 + - group: {{ user }} + - mode: 640 -{{ home }}/livelog.key: +{{ etc }}/livelog.key: file.managed: - contents_pillar: livelog_key - - user: {{ user }} - - mode: 600 + - group: {{ user }} + - mode: 640 {{ bin }}/generic-worker new-openpgp-keypair --file {{ home }}/key: cmd.run: - creates: {{ home }}/key - - runas: worker - -{{ home }}/run: - file.managed: - - mode: 744 - - user: {{ user }} - - template: jinja - - contents: |- - #!/bin/sh - # generic-worker overwrites its config file to fill in defaults, - # but we want to avoid touching config.json here - # so that SaltStack knows to (only) restart the service when it (really) changes. - cp -a config.json config-run.json - exec {{ bin }}/generic-worker run --config config-run.json + - runas: {{ user }} /Library/LaunchAgents/net.generic.worker.plist: file.managed: @@ -93,7 +87,10 @@ ProgramArguments - {{ home }}/run + {{ bin }}/generic-worker + run + --config + {{ etc }}/config.json KeepAlive From ef4e72ca7d018ae59f3abe6650ce09c2aeadd677 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 12 Nov 2018 23:42:57 +0100 Subject: [PATCH 07/10] generic-worker on macOS: move generic-worker.plist template to a separate file --- .../macos/states/generic-worker.plist.jinja | 18 +++++++++ .../macos/states/generic-worker.sls | 38 +++---------------- etc/taskcluster/macos/states/top.sls | 2 +- 3 files changed, 25 insertions(+), 33 deletions(-) create mode 100644 etc/taskcluster/macos/states/generic-worker.plist.jinja diff --git a/etc/taskcluster/macos/states/generic-worker.plist.jinja b/etc/taskcluster/macos/states/generic-worker.plist.jinja new file mode 100644 index 00000000000..39fa43a5d87 --- /dev/null +++ b/etc/taskcluster/macos/states/generic-worker.plist.jinja @@ -0,0 +1,18 @@ + + + + + Label net.generic.worker + KeepAlive + StandardOutPath stdout.log + StandardErrorPath stderr.log + WorkingDirectory {{ home }} + UserName {{ user }} + ProgramArguments + {{ bin }}/generic-worker + run + --config + {{ etc }}/config.json + + + \ No newline at end of file diff --git a/etc/taskcluster/macos/states/generic-worker.sls b/etc/taskcluster/macos/states/generic-worker.sls index 3bacbabe204..accc7860752 100644 --- a/etc/taskcluster/macos/states/generic-worker.sls +++ b/etc/taskcluster/macos/states/generic-worker.sls @@ -77,38 +77,12 @@ file.managed: - mode: 644 - template: jinja - - contents: >- - - - - - Label - net.generic.worker - - ProgramArguments - - {{ bin }}/generic-worker - run - --config - {{ etc }}/config.json - - - KeepAlive - - - WorkingDirectory - {{ home }} - - UserName - {{ user }} - - StandardOutPath - stdout.log - - StandardErrorPath - stderr.log - - + - source: salt://generic-worker.plist.jinja + - context: + bin: {{ bin }} + etc: {{ etc }} + home: {{ home }} + user: {{ user }} net.generic.worker: service.running: diff --git a/etc/taskcluster/macos/states/top.sls b/etc/taskcluster/macos/states/top.sls index 79f7a40eed3..f72c130e427 100644 --- a/etc/taskcluster/macos/states/top.sls +++ b/etc/taskcluster/macos/states/top.sls @@ -1,3 +1,3 @@ base: 'mac*': - - generic-worker \ No newline at end of file + - generic-worker From 2017bc89318338693f05fdcdc8eb1adb6bd82744 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 12 Nov 2018 23:48:34 +0100 Subject: [PATCH 08/10] UTC --- etc/taskcluster/macos/states/generic-worker.sls | 3 +++ 1 file changed, 3 insertions(+) diff --git a/etc/taskcluster/macos/states/generic-worker.sls b/etc/taskcluster/macos/states/generic-worker.sls index accc7860752..83f4561deff 100644 --- a/etc/taskcluster/macos/states/generic-worker.sls +++ b/etc/taskcluster/macos/states/generic-worker.sls @@ -3,6 +3,9 @@ {% set user = "worker" %} {% set home = "/Users/" + user %} +GMT: + timezone.system + {{ bin }}/generic-worker: file.managed: - name: From 79bd98b67935869b48bc5869352be1d4cfdff566 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 13 Nov 2018 14:58:10 +0100 Subject: [PATCH 09/10] generic-worker on macOS: more docs, move minion config out of roster --- etc/taskcluster/macos/README.md | 16 +++++++++++++--- etc/taskcluster/macos/config/master | 6 ++++++ etc/taskcluster/macos/config/roster | 6 ------ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/etc/taskcluster/macos/README.md b/etc/taskcluster/macos/README.md index 0e5b1633701..287d1010f1e 100644 --- a/etc/taskcluster/macos/README.md +++ b/etc/taskcluster/macos/README.md @@ -15,10 +15,20 @@ cd etc/taskcluster/macos ./salt-ssh '*' state.apply test=True ``` -## Servers +## (Re)deploying a server -Servers are provisioned manually from MacStadium. -The `config/roster` file lists them by DNS name. +* Place an order or file a ticket with MacStadium to get a new hardware or reinstall an OS. + +* Change the administrator password to one generated with + ` Date: Tue, 13 Nov 2018 15:30:50 +0100 Subject: [PATCH 10/10] Taskcluster: Add dev build and unit tests on macOS --- etc/taskcluster/decision_task.py | 71 ++++++++++++++++++++++++++++++-- etc/taskcluster/decisionlib.py | 55 ++++++++++++++++++++++++- 2 files changed, 121 insertions(+), 5 deletions(-) diff --git a/etc/taskcluster/decision_task.py b/etc/taskcluster/decision_task.py index 509503123ce..faa07883547 100644 --- a/etc/taskcluster/decision_task.py +++ b/etc/taskcluster/decision_task.py @@ -13,7 +13,8 @@ def main(task_for, mock=False): if CONFIG.git_ref in ["refs/heads/auto", "refs/heads/try", "refs/heads/try-taskcluster"]: linux_tidy_unit() android_arm32() - windows_dev() + windows_unit() + macos_unit() if mock: windows_release() linux_wpt() @@ -40,12 +41,15 @@ build_env = { "RUSTFLAGS": "-Dwarnings", "CARGO_INCREMENTAL": "0", } -linux_build_env = { +unix_build_env = { "CCACHE": "sccache", "RUSTC_WRAPPER": "sccache", "SCCACHE_IDLE_TIMEOUT": "1200", +} +linux_build_env = { "SHELL": "/bin/dash", # For SpiderMonkey’s build system } +macos_build_env = {} windows_build_env = { "LIB": "%HOMEDRIVE%%HOMEPATH%\\gst\\gstreamer\\1.0\\x86_64\\lib;%LIB%", } @@ -73,6 +77,15 @@ def linux_tidy_unit(): """).create() +def macos_unit(): + macos_build_task("macOS x64: dev build + unit tests").with_script(""" + ./mach build --dev + ./mach test-unit + ./mach package --dev + ./etc/ci/lockfile_changed.sh + """).create() + + def with_rust_nightly(): return linux_build_task("Linux x64: with Rust Nightly").with_script(""" echo "nightly" > rust-toolchain @@ -123,7 +136,7 @@ def android_x86(): ) -def windows_dev(): +def windows_unit(): return ( windows_build_task("Windows x64: dev build + unit tests") .with_script( @@ -268,6 +281,14 @@ def windows_task(name): return WindowsGenericWorkerTask(name).with_worker_type("servo-win2016") +def macos_task(name): + return ( + MacOsGenericWorkerTask(name) + .with_provisioner_id("proj-servo") + .with_worker_type("macos") + ) + + def linux_build_task(name): return ( linux_task(name) @@ -283,7 +304,7 @@ def linux_build_task(name): .with_index_and_artifacts_expire_in(build_artifacts_expire_in) .with_max_run_time_minutes(60) .with_dockerfile(dockerfile_path("build")) - .with_env(**build_env, **linux_build_env) + .with_env(**build_env, **unix_build_env, **linux_build_env) .with_repo() .with_index_and_artifacts_expire_in(build_artifacts_expire_in) ) @@ -325,6 +346,48 @@ def windows_build_task(name): ) +def macos_build_task(name): + return ( + macos_task(name) + # Allow long runtime in case the cache expired for all those Homebrew dependencies + .with_max_run_time_minutes(60 * 2) + .with_env(**build_env, **unix_build_env, **macos_build_env) + .with_repo() + .with_python2() + .with_rustup() + .with_script(""" + mkdir -p "$HOME/homebrew" + export PATH="$HOME/homebrew/bin:$PATH" + which homebrew || curl -L https://github.com/Homebrew/brew/tarball/master \ + | tar xz --strip 1 -C "$HOME/homebrew" + + time brew install automake autoconf@2.13 pkg-config cmake yasm llvm + time brew install gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad \ + gst-libav gst-rtsp-server \ + --with-orc --with-libogg --with-opus --with-pango --with-theora --with-libvorbis + export OPENSSL_INCLUDE_DIR="$(brew --prefix openssl)/include" + export OPENSSL_LIB_DIR="$(brew --prefix openssl)/lib" + """) + + .with_directory_mount( + "https://github.com/mozilla/sccache/releases/download/" + "0.2.7/sccache-0.2.7-x86_64-apple-darwin.tar.gz", + sha256="f86412abbbcce2d3f23e7d33305469198949f5cf807e6c3258c9e1885b4cb57f", + path="sccache", + ) + # Early script in order to run with the initial $PWD + .with_early_script(""" + export PATH="$PWD/sccache/sccache-0.2.7-x86_64-apple-darwin:$PATH" + """) + # sccache binaries requires OpenSSL 1.1 and are not compatible with 1.0. + # "Late" script in order to run after Homebrew is installed. + .with_script(""" + time brew install openssl@1.1 + export DYLD_LIBRARY_PATH="$HOME/homebrew/opt/openssl@1.1/lib" + """) + ) + + CONFIG.task_name_template = "Servo: %s" CONFIG.index_prefix = "project.servo.servo" CONFIG.docker_image_buil_worker_type = "servo-docker-worker" diff --git a/etc/taskcluster/decisionlib.py b/etc/taskcluster/decisionlib.py index 3dd1af32a3c..81b6ec6af25 100644 --- a/etc/taskcluster/decisionlib.py +++ b/etc/taskcluster/decisionlib.py @@ -27,7 +27,7 @@ import taskcluster # Public API __all__ = [ "CONFIG", "SHARED", "Task", "DockerWorkerTask", - "GenericWorkerTask", "WindowsGenericWorkerTask", + "GenericWorkerTask", "WindowsGenericWorkerTask", "MacOsGenericWorkerTask", ] @@ -496,6 +496,59 @@ class WindowsGenericWorkerTask(GenericWorkerTask): .with_path_from_homedir("python2", "python2\\Scripts") +class MacOsGenericWorkerTask(GenericWorkerTask): + """ + Task definition for a `generic-worker` task running on macOS. + + Scripts are interpreted with `bash`. + """ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.scripts = [] + + with_script = chaining(append_to_attr, "scripts") + with_early_script = chaining(prepend_to_attr, "scripts") + + def build_command(self): + # generic-worker accepts multiple commands, but unlike on Windows + # the current directory and environment variables + # are not preserved across commands on macOS. + # So concatenate scripts and use a single `bash` command instead. + return [ + [ + "/bin/bash", "--login", "-x", "-e", "-c", + deindent("\n".join(self.scripts)) + ] + ] + + def with_repo(self): + """ + Make a shallow clone the git repository at the start of the task. + This uses `CONFIG.git_url`, `CONFIG.git_ref`, and `CONFIG.git_sha`, + and creates the clone in a `repo` directory in the task’s directory. + """ + return self \ + .with_env(**git_env()) \ + .with_early_script(""" + git init repo + cd repo + git fetch --depth 1 "$GIT_URL" "$GIT_REF" + git reset --hard "$GIT_SHA" + """) + + def with_python2(self): + return self.with_early_script(""" + export PATH="$HOME/Library/Python/2.7/bin:$PATH" + python -m ensurepip --user + pip install --user virtualenv + """) + + def with_rustup(self): + return self.with_early_script(""" + export PATH="$HOME/.cargo/bin:$PATH" + which rustup || curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain none -y + """) + class DockerWorkerTask(Task): """