Show More
@@ -1,205 +1,218 | |||||
1 | ==================== |
|
1 | ==================== | |
2 | Mercurial Automation |
|
2 | Mercurial Automation | |
3 | ==================== |
|
3 | ==================== | |
4 |
|
4 | |||
5 | This directory contains code and utilities for building and testing Mercurial |
|
5 | This directory contains code and utilities for building and testing Mercurial | |
6 | on remote machines. |
|
6 | on remote machines. | |
7 |
|
7 | |||
8 | The ``automation.py`` Script |
|
8 | The ``automation.py`` Script | |
9 | ============================ |
|
9 | ============================ | |
10 |
|
10 | |||
11 | ``automation.py`` is an executable Python script (requires Python 3.5+) |
|
11 | ``automation.py`` is an executable Python script (requires Python 3.5+) | |
12 | that serves as a driver to common automation tasks. |
|
12 | that serves as a driver to common automation tasks. | |
13 |
|
13 | |||
14 | When executed, the script will *bootstrap* a virtualenv in |
|
14 | When executed, the script will *bootstrap* a virtualenv in | |
15 | ``<source-root>/build/venv-automation`` then re-execute itself using |
|
15 | ``<source-root>/build/venv-automation`` then re-execute itself using | |
16 | that virtualenv. So there is no need for the caller to have a virtualenv |
|
16 | that virtualenv. So there is no need for the caller to have a virtualenv | |
17 | explicitly activated. This virtualenv will be populated with various |
|
17 | explicitly activated. This virtualenv will be populated with various | |
18 | dependencies (as defined by the ``requirements.txt`` file). |
|
18 | dependencies (as defined by the ``requirements.txt`` file). | |
19 |
|
19 | |||
20 | To see what you can do with this script, simply run it:: |
|
20 | To see what you can do with this script, simply run it:: | |
21 |
|
21 | |||
22 | $ ./automation.py |
|
22 | $ ./automation.py | |
23 |
|
23 | |||
24 | Local State |
|
24 | Local State | |
25 | =========== |
|
25 | =========== | |
26 |
|
26 | |||
27 | By default, local state required to interact with remote servers is stored |
|
27 | By default, local state required to interact with remote servers is stored | |
28 | in the ``~/.hgautomation`` directory. |
|
28 | in the ``~/.hgautomation`` directory. | |
29 |
|
29 | |||
30 | We attempt to limit persistent state to this directory. Even when |
|
30 | We attempt to limit persistent state to this directory. Even when | |
31 | performing tasks that may have side-effects, we try to limit those |
|
31 | performing tasks that may have side-effects, we try to limit those | |
32 | side-effects so they don't impact the local system. e.g. when we SSH |
|
32 | side-effects so they don't impact the local system. e.g. when we SSH | |
33 | into a remote machine, we create a temporary directory for the SSH |
|
33 | into a remote machine, we create a temporary directory for the SSH | |
34 | config so the user's known hosts file isn't updated. |
|
34 | config so the user's known hosts file isn't updated. | |
35 |
|
35 | |||
36 | AWS Integration |
|
36 | AWS Integration | |
37 | =============== |
|
37 | =============== | |
38 |
|
38 | |||
39 | Various automation tasks integrate with AWS to provide access to |
|
39 | Various automation tasks integrate with AWS to provide access to | |
40 | resources such as EC2 instances for generic compute. |
|
40 | resources such as EC2 instances for generic compute. | |
41 |
|
41 | |||
42 | This obviously requires an AWS account and credentials to work. |
|
42 | This obviously requires an AWS account and credentials to work. | |
43 |
|
43 | |||
44 | We use the ``boto3`` library for interacting with AWS APIs. We do not employ |
|
44 | We use the ``boto3`` library for interacting with AWS APIs. We do not employ | |
45 | any special functionality for telling ``boto3`` where to find AWS credentials. See |
|
45 | any special functionality for telling ``boto3`` where to find AWS credentials. See | |
46 | https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html |
|
46 | https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html | |
47 | for how ``boto3`` works. Once you have configured your environment such |
|
47 | for how ``boto3`` works. Once you have configured your environment such | |
48 | that ``boto3`` can find credentials, interaction with AWS should *just work*. |
|
48 | that ``boto3`` can find credentials, interaction with AWS should *just work*. | |
49 |
|
49 | |||
50 | .. hint:: |
|
50 | To configure ``boto3``, you can use the ``aws configure`` command to | |
|
51 | write out configuration files. (The ``aws`` command is typically provided | |||
|
52 | by an ``awscli`` package available in your package manager, including | |||
|
53 | ``pip``.) Alternatively, you can write out files in ``~/.aws/`` directly. | |||
|
54 | e.g.:: | |||
|
55 | ||||
|
56 | # ~/.aws/config | |||
|
57 | [default] | |||
|
58 | region = us-west-2 | |||
51 |
|
|
59 | ||
52 | Typically you have a ``~/.aws/credentials`` file containing AWS |
|
60 | # ~/.aws/credentials | |
53 | credentials. If you manage multiple credentials, you can override which |
|
61 | [default] | |
54 | *profile* to use at run-time by setting the ``AWS_PROFILE`` environment |
|
62 | aws_access_key_id = XXXX | |
55 | variable. |
|
63 | aws_secret_access_key = YYYY | |
|
64 | ||||
|
65 | If you have multiple AWS accounts, you can name the profile something | |||
|
66 | different from ``default``. e.g. ``hg``. You can influence which profile | |||
|
67 | is used by ``boto3`` by setting the ``AWS_PROFILE`` environment variable. | |||
|
68 | e.g. ``AWS_PROFILE=hg``. | |||
56 |
|
69 | |||
57 | Resource Management |
|
70 | Resource Management | |
58 | ------------------- |
|
71 | ------------------- | |
59 |
|
72 | |||
60 | Depending on the task being performed, various AWS services will be accessed. |
|
73 | Depending on the task being performed, various AWS services will be accessed. | |
61 | This of course requires AWS credentials with permissions to access these |
|
74 | This of course requires AWS credentials with permissions to access these | |
62 | services. |
|
75 | services. | |
63 |
|
76 | |||
64 | The following AWS services can be accessed by automation tasks: |
|
77 | The following AWS services can be accessed by automation tasks: | |
65 |
|
78 | |||
66 | * EC2 |
|
79 | * EC2 | |
67 | * IAM |
|
80 | * IAM | |
68 | * Simple Systems Manager (SSM) |
|
81 | * Simple Systems Manager (SSM) | |
69 |
|
82 | |||
70 | Various resources will also be created as part of performing various tasks. |
|
83 | Various resources will also be created as part of performing various tasks. | |
71 | This also requires various permissions. |
|
84 | This also requires various permissions. | |
72 |
|
85 | |||
73 | The following AWS resources can be created by automation tasks: |
|
86 | The following AWS resources can be created by automation tasks: | |
74 |
|
87 | |||
75 | * EC2 key pairs |
|
88 | * EC2 key pairs | |
76 | * EC2 security groups |
|
89 | * EC2 security groups | |
77 | * EC2 instances |
|
90 | * EC2 instances | |
78 | * IAM roles and instance profiles |
|
91 | * IAM roles and instance profiles | |
79 | * SSM command invocations |
|
92 | * SSM command invocations | |
80 |
|
93 | |||
81 | When possible, we prefix resource names with ``hg-`` so they can easily |
|
94 | When possible, we prefix resource names with ``hg-`` so they can easily | |
82 | be identified as belonging to Mercurial. |
|
95 | be identified as belonging to Mercurial. | |
83 |
|
96 | |||
84 | .. important:: |
|
97 | .. important:: | |
85 |
|
98 | |||
86 | We currently assume that AWS accounts utilized by *us* are single |
|
99 | We currently assume that AWS accounts utilized by *us* are single | |
87 | tenancy. Attempts to have discrete users of ``automation.py`` (including |
|
100 | tenancy. Attempts to have discrete users of ``automation.py`` (including | |
88 | sharing credentials across machines) using the same AWS account can result |
|
101 | sharing credentials across machines) using the same AWS account can result | |
89 | in them interfering with each other and things breaking. |
|
102 | in them interfering with each other and things breaking. | |
90 |
|
103 | |||
91 | Cost of Operation |
|
104 | Cost of Operation | |
92 | ----------------- |
|
105 | ----------------- | |
93 |
|
106 | |||
94 | ``automation.py`` tries to be frugal with regards to utilization of remote |
|
107 | ``automation.py`` tries to be frugal with regards to utilization of remote | |
95 | resources. Persistent remote resources are minimized in order to keep costs |
|
108 | resources. Persistent remote resources are minimized in order to keep costs | |
96 | in check. For example, EC2 instances are often ephemeral and only live as long |
|
109 | in check. For example, EC2 instances are often ephemeral and only live as long | |
97 | as the operation being performed. |
|
110 | as the operation being performed. | |
98 |
|
111 | |||
99 | Under normal operation, recurring costs are limited to: |
|
112 | Under normal operation, recurring costs are limited to: | |
100 |
|
113 | |||
101 | * Storage costs for AMI / EBS snapshots. This should be just a few pennies |
|
114 | * Storage costs for AMI / EBS snapshots. This should be just a few pennies | |
102 | per month. |
|
115 | per month. | |
103 |
|
116 | |||
104 | When running EC2 instances, you'll be billed accordingly. Default instance |
|
117 | When running EC2 instances, you'll be billed accordingly. Default instance | |
105 | types vary by operation. We try to be respectful of your money when choosing |
|
118 | types vary by operation. We try to be respectful of your money when choosing | |
106 | defaults. e.g. for Windows instances which are billed per hour, we use e.g. |
|
119 | defaults. e.g. for Windows instances which are billed per hour, we use e.g. | |
107 | ``t3.medium`` instances, which cost ~$0.07 per hour. For operations that |
|
120 | ``t3.medium`` instances, which cost ~$0.07 per hour. For operations that | |
108 | scale well to many CPUs like running Linux tests, we may use a more powerful |
|
121 | scale well to many CPUs like running Linux tests, we may use a more powerful | |
109 | instance like ``c5.9xlarge``. However, since Linux instances are billed |
|
122 | instance like ``c5.9xlarge``. However, since Linux instances are billed | |
110 | per second and the cost of running an e.g. ``c5.9xlarge`` for half the time |
|
123 | per second and the cost of running an e.g. ``c5.9xlarge`` for half the time | |
111 | of a ``c5.4xlarge`` is roughly the same, the choice is justified. |
|
124 | of a ``c5.4xlarge`` is roughly the same, the choice is justified. | |
112 |
|
125 | |||
113 | .. note:: |
|
126 | .. note:: | |
114 |
|
127 | |||
115 | When running Windows EC2 instances, AWS bills at the full hourly cost, even |
|
128 | When running Windows EC2 instances, AWS bills at the full hourly cost, even | |
116 | if the instance doesn't run for a full hour (per-second billing doesn't |
|
129 | if the instance doesn't run for a full hour (per-second billing doesn't | |
117 | apply to Windows AMIs). |
|
130 | apply to Windows AMIs). | |
118 |
|
131 | |||
119 | Managing Remote Resources |
|
132 | Managing Remote Resources | |
120 | ------------------------- |
|
133 | ------------------------- | |
121 |
|
134 | |||
122 | Occassionally, there may be an error purging a temporary resource. Or you |
|
135 | Occassionally, there may be an error purging a temporary resource. Or you | |
123 | may wish to forcefully purge remote state. Commands can be invoked to manually |
|
136 | may wish to forcefully purge remote state. Commands can be invoked to manually | |
124 | purge remote resources. |
|
137 | purge remote resources. | |
125 |
|
138 | |||
126 | To terminate all EC2 instances that we manage:: |
|
139 | To terminate all EC2 instances that we manage:: | |
127 |
|
140 | |||
128 | $ automation.py terminate-ec2-instances |
|
141 | $ automation.py terminate-ec2-instances | |
129 |
|
142 | |||
130 | To purge all EC2 resources that we manage:: |
|
143 | To purge all EC2 resources that we manage:: | |
131 |
|
144 | |||
132 | $ automation.py purge-ec2-resources |
|
145 | $ automation.py purge-ec2-resources | |
133 |
|
146 | |||
134 | Remote Machine Interfaces |
|
147 | Remote Machine Interfaces | |
135 | ========================= |
|
148 | ========================= | |
136 |
|
149 | |||
137 | The code that connects to a remote machine and executes things is |
|
150 | The code that connects to a remote machine and executes things is | |
138 | theoretically machine agnostic as long as the remote machine conforms to |
|
151 | theoretically machine agnostic as long as the remote machine conforms to | |
139 | an *interface*. In other words, to perform actions like running tests |
|
152 | an *interface*. In other words, to perform actions like running tests | |
140 | remotely or triggering packaging, it shouldn't matter if the remote machine |
|
153 | remotely or triggering packaging, it shouldn't matter if the remote machine | |
141 | is an EC2 instance, a virtual machine, etc. This section attempts to document |
|
154 | is an EC2 instance, a virtual machine, etc. This section attempts to document | |
142 | the interface that remote machines need to provide in order to be valid |
|
155 | the interface that remote machines need to provide in order to be valid | |
143 | *targets* for remote execution. These interfaces are often not ideal nor |
|
156 | *targets* for remote execution. These interfaces are often not ideal nor | |
144 | the most flexible. Instead, they have often evolved as the requirements of |
|
157 | the most flexible. Instead, they have often evolved as the requirements of | |
145 | our automation code have evolved. |
|
158 | our automation code have evolved. | |
146 |
|
159 | |||
147 | Linux |
|
160 | Linux | |
148 | ----- |
|
161 | ----- | |
149 |
|
162 | |||
150 | Remote Linux machines expose an SSH server on port 22. The SSH server |
|
163 | Remote Linux machines expose an SSH server on port 22. The SSH server | |
151 | must allow the ``hg`` user to authenticate using the SSH key generated by |
|
164 | must allow the ``hg`` user to authenticate using the SSH key generated by | |
152 | the automation code. The ``hg`` user should be part of the ``hg`` group |
|
165 | the automation code. The ``hg`` user should be part of the ``hg`` group | |
153 | and it should have ``sudo`` access without password prompting. |
|
166 | and it should have ``sudo`` access without password prompting. | |
154 |
|
167 | |||
155 | The SSH channel must support SFTP to facilitate transferring files from |
|
168 | The SSH channel must support SFTP to facilitate transferring files from | |
156 | client to server. |
|
169 | client to server. | |
157 |
|
170 | |||
158 | ``/bin/bash`` must be executable and point to a bash shell executable. |
|
171 | ``/bin/bash`` must be executable and point to a bash shell executable. | |
159 |
|
172 | |||
160 | The ``/hgdev`` directory must exist and all its content owned by ``hg::hg``. |
|
173 | The ``/hgdev`` directory must exist and all its content owned by ``hg::hg``. | |
161 |
|
174 | |||
162 | The ``/hgdev/pyenv`` directory should contain an installation of |
|
175 | The ``/hgdev/pyenv`` directory should contain an installation of | |
163 | ``pyenv``. Various Python distributions should be installed. The exact |
|
176 | ``pyenv``. Various Python distributions should be installed. The exact | |
164 | versions shouldn't matter. ``pyenv global`` should have been run so |
|
177 | versions shouldn't matter. ``pyenv global`` should have been run so | |
165 | ``/hgdev/pyenv/shims/`` is populated with redirector scripts that point |
|
178 | ``/hgdev/pyenv/shims/`` is populated with redirector scripts that point | |
166 | to the appropriate Python executable. |
|
179 | to the appropriate Python executable. | |
167 |
|
180 | |||
168 | The ``/hgdev/venv-bootstrap`` directory must contain a virtualenv |
|
181 | The ``/hgdev/venv-bootstrap`` directory must contain a virtualenv | |
169 | with Mercurial installed. The ``/hgdev/venv-bootstrap/bin/hg`` executable |
|
182 | with Mercurial installed. The ``/hgdev/venv-bootstrap/bin/hg`` executable | |
170 | is referenced by various scripts and the client. |
|
183 | is referenced by various scripts and the client. | |
171 |
|
184 | |||
172 | The ``/hgdev/src`` directory MUST contain a clone of the Mercurial |
|
185 | The ``/hgdev/src`` directory MUST contain a clone of the Mercurial | |
173 | source code. The state of the working directory is not important. |
|
186 | source code. The state of the working directory is not important. | |
174 |
|
187 | |||
175 | In order to run tests, the ``/hgwork`` directory will be created. |
|
188 | In order to run tests, the ``/hgwork`` directory will be created. | |
176 | This may require running various ``mkfs.*`` executables and ``mount`` |
|
189 | This may require running various ``mkfs.*`` executables and ``mount`` | |
177 | to provision a new filesystem. This will require elevated privileges |
|
190 | to provision a new filesystem. This will require elevated privileges | |
178 | via ``sudo``. |
|
191 | via ``sudo``. | |
179 |
|
192 | |||
180 | Various dependencies to run the Mercurial test harness are also required. |
|
193 | Various dependencies to run the Mercurial test harness are also required. | |
181 | Documenting them is beyond the scope of this document. Various tests |
|
194 | Documenting them is beyond the scope of this document. Various tests | |
182 | also require other optional dependencies and missing dependencies will |
|
195 | also require other optional dependencies and missing dependencies will | |
183 | be printed by the test runner when a test is skipped. |
|
196 | be printed by the test runner when a test is skipped. | |
184 |
|
197 | |||
185 | Releasing Windows Artifacts |
|
198 | Releasing Windows Artifacts | |
186 | =========================== |
|
199 | =========================== | |
187 |
|
200 | |||
188 | The `automation.py` script can be used to automate the release of Windows |
|
201 | The `automation.py` script can be used to automate the release of Windows | |
189 | artifacts:: |
|
202 | artifacts:: | |
190 |
|
203 | |||
191 | $ ./automation.py build-all-windows-packages --revision 5.1.1 |
|
204 | $ ./automation.py build-all-windows-packages --revision 5.1.1 | |
192 | $ ./automation.py publish-windows-artifacts 5.1.1 |
|
205 | $ ./automation.py publish-windows-artifacts 5.1.1 | |
193 |
|
206 | |||
194 | The first command will launch an EC2 instance to build all Windows packages |
|
207 | The first command will launch an EC2 instance to build all Windows packages | |
195 | and copy them into the `dist` directory relative to the repository root. The |
|
208 | and copy them into the `dist` directory relative to the repository root. The | |
196 | second command will then attempt to upload these files to PyPI (via `twine`) |
|
209 | second command will then attempt to upload these files to PyPI (via `twine`) | |
197 | and to `mercurial-scm.org` (via SSH). |
|
210 | and to `mercurial-scm.org` (via SSH). | |
198 |
|
211 | |||
199 | Uploading to PyPI requires a PyPI account with write access to the `Mercurial` |
|
212 | Uploading to PyPI requires a PyPI account with write access to the `Mercurial` | |
200 | package. You can skip PyPI uploading by passing `--no-pypi`. |
|
213 | package. You can skip PyPI uploading by passing `--no-pypi`. | |
201 |
|
214 | |||
202 | Uploading to `mercurial-scm.org` requires an SSH account on that server |
|
215 | Uploading to `mercurial-scm.org` requires an SSH account on that server | |
203 | with `windows` group membership and for the SSH key for that account to be the |
|
216 | with `windows` group membership and for the SSH key for that account to be the | |
204 | default SSH key (e.g. `~/.ssh/id_rsa`) or in a running SSH agent. You can |
|
217 | default SSH key (e.g. `~/.ssh/id_rsa`) or in a running SSH agent. You can | |
205 | skip `mercurial-scm.org` uploading by passing `--no-mercurial-scm-org`. |
|
218 | skip `mercurial-scm.org` uploading by passing `--no-mercurial-scm-org`. |
General Comments 0
You need to be logged in to leave comments.
Login now