Commit Graph

259 Commits

Author SHA1 Message Date
Zuul b2c0d69cbc Merge "Add a zuul.buildset_refs variable" 2024-03-25 10:09:16 +00:00
James E. Blair 632839804c Add a zuul.buildset_refs variable
This adds information about the changes associated with a
circular dependency queue item.  Currently the bundle_id can be
used to identify which of the items in zuul.items is related to
the current dependency cycle.  That variable is deprecated, so
zuul.buildset_refs can be used to replace that functionality.

Since it repeats some of the information at the top level (eg
zuul.change, zuul.project, etc), the code is refactored so they
can share the dictionary construction.  That is also used by
zuul.items.  This results in a few extra fields in zuul.items
now, such as the change message, but that is relatively
inconsequential, so is not called out in the release notes.

The src_dir is similarly included in all of these places.  In
writing this change it was discovered that
zuul.items.project.src_dir always used the golang scheme, but
zuul.project.src_dir used the correct per-job workspace scheme.
This has been corrected so that they both use the per-job scheme
now.

A significant reorganization of the job variable documentation is
included.  Previously we had a section with additional variables
for each item type, but since many of these are duplicated at the
top level, in the item list, and now in the refs list, that
structure became difficult to work with.  Instead, the
documentation for each of these sections now exhaustively lists
all of the possible variables.  This makes for some repitition,
but it also means that a user can see at a glance what variables
are available, and we can deep-link to each one.  To address the
variation between different item types, the variables that mutate
based on item type now contain a definition list indicating what
types they are valid for and their respective meanings.

Change-Id: Iab8f99d4c4f40c44d630120c458539060cc725b5
2024-03-22 06:41:36 -07:00
Benjamin Schanzel f9ebf6a1c9
Zuul-Web: substring search for builds, buildsets
Allow to search for builds and buildsets using substrings of job_name,
project, branch, and pipeline. This is done by placing wildcard
characters (*) into the filter string which get translated to SQL
wildcards (%), representing zero, one, or multiple characters.

Translating SQL style wildcards (%) to asterisks is done because
asterisks as wildcard chars might feel more intuitive, cf. shell file
globbing or regexp.

The SQL LIKE operator is only used if a wildcard is present in the
filter string. This is to not rely on the underlying SQL implementation
of optimizing queries with a LIKE op but no wildcard (ie. exact match),
so that we don't introduce unnecessary performance penalties.

Change-Id: I827a27915308f78fc01019bd988b34ea987c90ea
2024-03-12 13:58:01 +01:00
Zuul a23e05ac9e Merge "Change status json to use "refs" instead of "changes"" 2024-02-10 21:50:10 +00:00
Zuul 1beac435ab Merge "Finish circular dependency refactor" 2024-02-10 21:27:22 +00:00
James E. Blair 1cc276687a Change status json to use "refs" instead of "changes"
This is mostly an internal API change to replace the use of the
word "change" with "ref" in the status json.  This matches the
database and build/buildsets records.

Change-Id: Id468d16d6deb0af3d1c0f74beb1b25630455b8f9
2024-02-09 07:39:52 -08:00
James E. Blair 1f026bd49c Finish circular dependency refactor
This change completes the circular dependency refactor.

The principal change is that queue items may now include
more than one change simultaneously in the case of circular
dependencies.

In dependent pipelines, the two-phase reporting process is
simplified because it happens during processing of a single
item.

In independent pipelines, non-live items are still used for
linear depnedencies, but multi-change items are used for
circular dependencies.

Previously changes were enqueued recursively and then
bundles were made out of the resulting items.  Since we now
need to enqueue entire cycles in one queue item, the
dependency graph generation is performed at the start of
enqueing the first change in a cycle.

Some tests exercise situations where Zuul is processing
events for old patchsets of changes.  The new change query
sequence mentioned in the previous paragraph necessitates
more accurate information about out-of-date patchsets than
the previous sequence, therefore the Gerrit driver has been
updated to query and return more data about non-current
patchsets.

This change is not backwards compatible with the existing
ZK schema, and will require Zuul systems delete all pipeline
states during the upgrade.  A later change will implement
a helper command for this.

All backwards compatability handling for the last several
model_api versions which were added to prepare for this
upgrade have been removed.  In general, all model data
structures involving frozen jobs are now indexed by the
frozen job's uuid and no longer include the job name since
a job name no longer uniquely identifies a job in a buildset
(either the uuid or the (job name, change) tuple must be
used to identify it).

Job deduplication is simplified and now only needs to
consider jobs within the same buildset.

The fake github driver had a bug (fakegithub.py line 694) where
it did not correctly increment the check run counter, so our
tests that verified that we closed out obsolete check runs
when re-enqueing were not valid.  This has been corrected, and
in doing so, has necessitated some changes around quiet dequeing
when we re-enqueue a change.

The reporting in several drivers has been updated to support
reporting information about multiple changes in a queue item.

Change-Id: I0b9e4d3f9936b1e66a08142fc36866269dc287f1
Depends-On: https://review.opendev.org/907627
2024-02-09 07:39:40 -08:00
James E. Blair b038dcaf9f Deprecate Ansible 6
Ansible 6 is no longer supported and 8 is available and working.
Deprecate Ansible 6.

Change-Id: I721ae1659cc062d9938ceea863ad746996892cc7
2024-02-07 13:22:21 -08:00
James E. Blair 50f068ee6d Add a build-times web endpoint
This endpoint runs an optimized query for returning information
suitable for displaying a graph of build times.

This includes a schema migration to add some indexes to aid
the query.

Change-Id: I56e8422a599c1ee51216f26fcae5a39013066e6b
2024-01-03 13:06:07 -08:00
Zuul f51addbcdc Merge "Add tests for admin api token usage with access-rules" 2023-12-01 17:21:05 +00:00
James E. Blair 00949554c9 Implement server-side filtering and pagination of config errors
In order to support pagination of config errors (so that when a user
does decide to look at the config errors page, we don't necessarily
need to transfer all of the data each time, implement server-side
filtering and pagination.

A later change can implement the same in the web ui.

Change-Id: I0c6cb8a10cd4d807ed92cad438ef592b1cdaf19b
2023-11-20 14:17:16 -08:00
James E. Blair 8a2458aac6 Add tests for admin api token usage with access-rules
This change adds some more tests of the zuul admin api token
(both via zuul-client and just simulated rest api usage) with
read-only access-rules in place.

Change-Id: Idc2fbb50e6f35a080ad5e5aa214ea85cbca42f11
2023-10-25 10:17:14 -07:00
James E. Blair 18fb324f1e Add auth token to websocket
When making a websocket request, browsers do not send the
"Authorization" header.  Therefore if a Zuul tenant is run in
a configuration where authz is required for read-only access,
the websocket-based log streaming will always fail.

To correct this, we will remove the http request authz check
from the console-stream endpoint, and add an optional token
parameter to the websocket message payload.  The JS web app
will be responsible for sending the auth token in the payload,
and the web server will validate it if it is required for the
tenant.  Thanks to Andrei Dmitriev for this suggestion.

Since we essentially have two different authz code paths in
zuul-web now, in order to share as much code as possible, the
authz sequence is refactored in such a way that the final authz
check can be deferred.  First we create an AuthContext at the
start of the request which stores tenant and header information,
then the actual validation is performed in a separate step where
the token can optionally be provided.

In the http code path, we create the AuthContext and validate
immediately, using the Authorization header, and we do all of that
in the cherrypy tool at the start of the request.

In the websocket code path, we create the AuthContext as the
websocket handler is being created by the cherrypy request handler,
then we perform validation after receiving a message on the
websocket.  We use the token supplied from the request.

Error handling is adjusted so in the http code path, exceptions
that return appropriate http errors are raised, but in the
websocket path, these are caught and translated into websocket
close calls.

A related issue is that we perform no validation that the
streaming build log being requested belongs to the tenant via
which the request is being sent.  This was unecessary before
read-only access was an option, but now that it is, we should
check that a streaming build request arrives via the correct
tenant URL.  This change adjusts that as well.

During testing, it was noted that the tenant configuration syntax
allows admin-rules and access-rules to use the scalar-or-list
pattern, however some parts of the code assumed only lists.  The
configloader is updated to use scalar-or-list for both of those
values.

Change-Id: Ifd4c21bb1fe962bf23acb5b4f10b3bbaba61e63a
Co-Authored-By: Andrei Dmitriev <andrei.dmitriev@nokia.com>
2023-10-24 07:29:55 -07:00
James E. Blair 4ebf9296f3 Add tenant_status web endpoint
The config error list is getting longer, for everyone, especially
now that we are including warnings.  To avoid loading the entirety
when it's not necessary, add an API endpoint that simply returns
the number of config errors.  This can later be used by the web app
to highlight the blue bell without actually fetching the errors.

This endpoint is named tenant_status so that if we find more details
like this that we want to add in the future, we can extend the returned
dictionary with them.

It is not added to the tenant info endpoint because that is
unauthenticated and should not leak information about the tenant.

Change-Id: Ie11eb26dc38e28922ddabbca39d89cda7e763d13
2023-09-22 07:10:55 -07:00
James E. Blair 1b042ba4ab Add job failure output detection regexes
This allows users to trigger the new early failure detection by
matching regexes in the streaming job output.

For example, if a unit test job outputs something sufficiently
unique on failure, one could write a regex that matches that and
triggers the early failure detection before the playbook completes.

For hour-long unit test jobs, this could save a considerable amount
of time.

Note that this adds the google-re2 library to the Ansible venvs.  It
has manylinux wheels available, so is easy to install with
zuul-manage-ansible.  In Zuul itself, we use the fb-re2 library which
requires compilation and is therefore more difficult to use with
zuul-manage-ansible.  Presumably using fb-re2 to validate the syntax
and then later actually using google-re2 to run the regexes is
sufficient.  We may want to switch Zuul to use google-re2 later for
consistency.

Change-Id: Ifc9454767385de4c96e6da6d6f41bcb936aa24cd
2023-08-21 16:41:21 -07:00
Simon Westphahl 3b011296e6 Keep task stdout/stderr separate in result object
Combining stdout/stderr in the result can lead to problems when e.g.
the stdout of a task is used as an input for another task.

This is also different from the normal Ansible behavior and can be
surprising and hard to debug for users.

The new behavior is configurable and off by default to retain backward
compatibility.

Change-Id: Icaced970650913f9632a8db75a5970a38d3a6bc4
Co-Authored-By: James E. Blair <jim@acmegating.com>
2023-08-17 16:22:41 -07:00
Zuul 19ae1f2f8a Merge "Add config metadata to identify project-templates" 2023-07-24 07:00:51 +00:00
Jeremy Stanley 6ec55d9f39 Add config metadata to identify project-templates
Since project default branches were fixed in
Iddd5ef906c2bdbcc472615ffe25033c4ca961df7 the workaround in the
project detail view of the dashboard to distinguish actual project
configs from project-template configs will no longer work. Extend
the config model to incorporate a flag which indicates whether it
comes from a template or an actual project directly, and use that in
the dashboard rather than a (now broken) proxy value.

Change-Id: I2958a9bb01ca5261604773de742b6b652fca4cae
2023-06-07 03:24:03 +00:00
James E. Blair 9e0f2b5694 Fix setting autoholds through API with change supplied
The set autohold api endpoint incorrectly handled supplied values
such that if the user supplied a change without a ref it would always
use the default ref (.*).  This corrects the case handling and
adds tests.

Change-Id: I1ae14c327fd8fd2b866013d4d5078a9fbd85f843
2023-06-01 15:45:59 -07:00
Simon Westphahl 3b0cc02f11
Only use "master" as default branch if not set
So far we've used "master" as default branch when we encounter a project
configuration that doesn't have a default branch set. The problem here
is, that later project stanzas that do define a default branch won't
have any effect.

To fix this, we will only fall back on using "master" in the project
metadata if none of the project definitions declares a default branch.

Change-Id: Iddd5ef906c2bdbcc472615ffe25033c4ca961df7
2023-04-14 07:45:51 +02:00
James E. Blair 3780ed548c Unpin JWT and use integer IAT values
PyJWT 2.6.0 began performing validation of iat (issued at) claims
in 9cb9401cc5

I believe the intent of RFC7519 is to support any numeric values
(including floating point) for iat, nbf, and exp, however, the
PyJWT library has made the assumption that the values should be
integers, and therefore when we supply an iat with decimal seconds,
PyJWT will round down when validating the value. In our unit tests,
this can cause validation errors.

In order to avoid any issues, we will round down the times that
we supply when generating JWT tokens and supply them as integers
in accordance with the robustness principle.

Change-Id: Ia8341b4d5de827e2df8878f11f2d1f52a1243cd4
2022-11-15 13:52:53 -08:00
James E. Blair c355adf44e Add playbook semaphores
This adds the ability to specify that the Zuul executor should
acquire a semaphore before running an individual playbook.  This
is useful for long running jobs which need exclusive access to
a resources for only a small amount of time.

Change-Id: I90f5e0f570ef6c4b0986b0143318a78ddc27bbde
2022-11-07 08:41:10 -08:00
Zuul 7606304159 Merge "Change merge mode default based on driver" 2022-10-27 02:25:37 +00:00
James E. Blair 9d2e1339ff Support authz for read-only web access
This updates the web UI to support the requirement for authn/z
for read-only access.

If authz is required for read access, we will automatically redirect.
If we return and still aren't authorized, we will display an
"Authorization required" page (rather than continuing and popping up
API error notifications).

The API methods are updated to send an authorization token whenever
one is present.

Change-Id: I31c13c943d05819b4122fcbcf2eaf41515c5b1d9
2022-10-25 20:22:42 -07:00
James E. Blair c22f2c98e0 Add access-rules configuration and documentation
This allows configuration of read-only access rules, and corresponding
documentation.  It wraps every API method in an auth check (other than
info endpoints).

It exposes information in the info endpoints that the web UI can use
to decide whether it should send authentication information for all
requests.  A later change will update the web UI to use that.

Change-Id: I3985c3d0b9f831fd004b2bb010ab621c00486e05
2022-10-25 20:22:33 -07:00
James E. Blair 8c47d9ce4e Add api-root tenant config object
In order to allow for authenticated read-only access to zuul-web,
we need to be able to control the authz of the API root.  Currently,
we can only specify auth info for tenants.  But if we want to control
access to the tenant list itself, we need to be able to specify auth
rules.

To that end, add a new "api-root" tenant configuration object which,
like tenants themselves, will allow attaching authz rules to it.

We don't have any admin-level API endpoints at the root, so this change
does not add "admin-rules" to the api-root object, but if we do develop
those in the future, it could be added.

A later change will add "access-rules" to the api-root in order to
allow configuration of authenticated read-only access.

This change does add an "authentication-realm" to the api-root object
since that already exists for tenants and it will make sense to have
that in the future as well.  Currently the /info endpoint uses the
system default authentication realm, but this will override it if
set.

In general, the approach here is that the "api-root" object should
mirror the "tenant" object for all attributes that make sense.

Change-Id: I4efc6fbd64f266e7a10e101db3350837adce371f
2022-10-25 20:19:39 -07:00
James E. Blair 8f2dd91cbf Add check_auth tool to zuul-web
Authentication checking in the admin methods of zuul-web is very
duplicative.  Consolidate all of the auth checks into a cherrypy
tool that we can use to decorate methods.

This tool also anticipates that we will have read-only checks in
the future, but for now, it is still only used for admin checks.

This tool also populates some additional parameters (like tenant
and auth info) so that we don't need to call "getTenantOrRaise"
multiple times in a request.

Several methods performed HTTP method checks inside the method
which inhibits our ability to wrap an entire method with an
auth_check.  To resolve this, we now use method conditions on
the routes dispatcher.  As a convention, I have put the
options handling on the "GET" methods since they are most likely
to be universal.

Change-Id: Id815efd9337cbed621509bb0f914bdb552379bc7
2022-10-25 20:19:25 -07:00
Zuul 99959a3fa3 Merge "Simplify tenant_authorizatons check" 2022-10-26 02:21:25 +00:00
Zuul 75573b7aec Merge "Remove unused /api/user/authorizations REST endpoint" 2022-10-25 23:31:51 +00:00
Zuul e9a428bb91 Merge "Include skip reason in build error_detail" 2022-10-25 21:18:34 +00:00
Zuul b70d8de85b Merge "Include skipped builds in database and web ui" 2022-10-25 04:10:18 +00:00
James E. Blair e2a472bc97 Change merge mode default based on driver
The default merge mode is 'merge-resolve' because it has been observed
that it more closely matches the behavior of jgit in Gerrit (or, at
least it did the last time we looked into this).  The other drivers
are unlikely to use jgit and more likely to use the default git
merge strategy.

This change allows the default to differ based on the driver, and
changes the default for all non-gerrit drivers to 'merge'.

The implementation anticipates that we may want to add more granularity
in the future, so the API accepts a project as an argument, and in
the future, drivers could provide a per-project default (which they
may obtain from the remote code review system).  That is not implemented
yet.

This adds some extra data to the /projects endpoint in the REST api.
It is currently not easy (and perhaps not possible) to determine what a
project's merge mode is through the api.  This change adds a metadata
field to the output which will show the resulting value computed from
all of the project stanzas.  The project stanzas themselves may have
null values for the merge modes now, so the web app now protects against
that.

Change-Id: I9ddb79988ca08aba4662cd82124bd91e49fd053c
2022-10-13 10:31:19 -07:00
James E. Blair 81e9a51185 Deprecate Ansible 5, make Ansible 6 the default
Ansible 5 is no longer supported and 6 is available and working.
Deprecate Ansible 5.

Change-Id: I8c152f7c0818bccd07f50e85bef9a82ddb863a68
2022-10-11 16:56:57 -07:00
James E. Blair aeb777e77a Include skip reason in build error_detail
This will make available in the web ui and through the API the
reason for skipped builds.

This will also include more detail on the job line itself in
the report, like this:

- compile: SUCCESS in 3s
- pre-test: FAILURE in 3s
- test: SKIPPED Skipped due to failed job pre-test

Change-Id: Id5dd0c47cd524912fd3d2d0ecc659811f3ec01cb
2022-10-10 08:54:50 -07:00
James E. Blair 55ec721fa8 Simplify tenant_authorizatons check
This method iterates over all tenants but only needs to return
information about a single tenant.  Simplify the calculation for
efficiency.

This includes a change in behavior for unknown tenants.  Currently,
a request to /api/tenant/{name}/authorizations will always succeed
even if the tenant does not exist (it will return an authorization
entry indicating the user is not an admin of the unknown tenant).
This is unnecessary and confusing.  It will now return a 404 for
the unknown tenant.

In the updated unit test, tenant-two was an unknown tenant; its name
has been updated to 'unknown' to make that clear.

(Since the test asserted that data were returned either way, it is
unclear whether the original author of the unit test expected
tenant-two to be unknown or known.)

Change-Id: I545575fb73ef555b34c207f8a5f2e70935c049aa
2022-10-06 15:38:24 -07:00
James E. Blair 5e6dbf2001 Remove unused /api/user/authorizations REST endpoint
This has not beeen used for a while and can be removed.  This will
simplify the authorization code in zuul-web.

Change-Id: I0fa6c4fb87672c44d3f97db0be558737b4f102bc
2022-10-06 15:38:24 -07:00
James E. Blair 0738d31b08 Include skipped builds in database and web ui
We have had an on-and-off relationship with skipped builds in the
database.  Generally we have attempted to exclude them from the db,
but we have occasionally (accidentally?) included them.  The status
quo is that builds with a result of SKIPPED (as well as several
other results which don't come from the executor) are not recorded
in the database.

With a greater interest in being able to determine which jobs ran
or did not run for a change after the fact, this job deliberately
adds all builds (whether they touch an executor or not, whether
real or not) to the database.  This means than anything that could
potentially show up on the status page or in a code-review report
will be in the database, and can therefore be seen in the web UI.

It is still the case that we are not actually interested in seeing
a page full of SKIPPED builds when we visit the "Builds" tab in
the web ui (which is the principal reason we have not included them
in the database so far).  To address this, we set the default query
in the builds tab to exclude skipped builds (it is easy to add other
types of builds to exclude in the future if we wish).  If a user
then specifies a query filter to *include* specific results, we drop
the exclusion from the query string.  This allows for the expected
behavior of not showing SKIPPED by default, then as specific results
are added to the filter, we show only those, and if the user selects
that they want to see SKIPPED, they will then be included.

On the buildset page, we add a switch similar to the current "show
retried jobs" switch that selects whether skipped builds in a buildset
should be displayed (again, it hides them by default).

Change-Id: I1835965101299bc7a95c952e99f6b0b095def085
2022-10-06 13:28:02 -07:00
Zuul fd6af2931b Merge "Add semaphores to REST API" 2022-09-21 16:42:17 +00:00
Zuul e612a442e3 Merge "Add nodeset alternatives" 2022-09-16 16:52:44 +00:00
James E. Blair 06509d5e8a Remove deprecated pipeline queue configuration
This was deprecated quite some time ago and we should remove it as
part of the next major release.

Also remove a very old Zuul v1 layout.yaml from the test fixtures.

Change-Id: I40030840b71e95f813f028ff31bc3e9b3eac4d6a
2022-09-08 13:32:11 -07:00
James E. Blair 1958bbad03 Add nodeset alternatives
This feature instructs Zuul to attempt a second or more node request
with a different node configuration (ie, possibly different labels)
if the first one fails.

It is intended to address the case where a cloud provider is unable
to supply specialized high-performance nodes, and the user would like
the job to proceed anyway on lower-performance nodes.

Change-Id: Idede4244eaa3b21a34c20099214fda6ecdc992df
2022-09-08 13:01:28 -07:00
James E. Blair 06cfe2cacd Add semaphores to REST API
This adds information about semaphores to the REST API.

It allows for inspection of the known semaphores in a tenant, the
current number of jobs holding the semaphore, and information about
each holder iff that holder is in the current tenant.

Followup changes will add zuul-client and zuul-web support for the
API, along with docs and release notes.

Change-Id: I6ff57ca8db11add2429eefcc8b560abc9c074f4a
2022-09-07 14:28:12 -07:00
James E. Blair ad03402dec Deprecate Ansible 2, make Ansible 5 default
Change-Id: I2576d0dcec7c8f7bbb76bdd469fd992874742edc
2022-09-02 10:12:54 -07:00
Albin Vass 9e88f8e5cb Fix links for jobs with special characters
Change-Id: I12e8a056a2e5cd1bb18c1f24ecd7db55405f0a8c
2022-08-23 13:21:25 +02:00
Zuul 8434446c98 Merge "Handle jwt decoding error, fix exception default messages" 2022-07-14 18:29:09 +00:00
Zuul 40ca109994 Merge "Fix read-only branches error in zuul-web" 2022-07-06 17:38:18 +00:00
Simon Westphahl 41a4b80730 Fix flaky zuul-web tests using cached tenant list
Disable caching for the remaining zuul-web tests that use the tenant
list.

Change-Id: I03c80a2171ee02fba41319b21f78003e4c8554b4
2022-07-04 15:59:58 +02:00
Simon Westphahl c98f14025a Fix read-only branches error in zuul-web
When exclude-unprotected-branche is in effect and a project doesn't have
any protected branches (e.g. a wrong branch protection rule in Github or
none at all) the branch cache will contain an empty list.

Since the empty list was so far used to indicate a fetch error, those
projects showed up with a config error in zuul-web ("Will not fetch
project branches as read-only is set").

For the branch cache we need to distinguish three different cases:
1. branche cache miss (no fetch yet)
2. branch cache hit (including empty list of branches)
3. fetch error

Instead of storing an empty list in case of a fetch error we will store
a sentinel value of `None` in those cases. However, with this change we
can't use `None` as an indicator for a cache miss anymore, so we are now
raising a `LookupError` instead.

Change-Id: I5b51805f7d0a5d916a46fe89db7b32f14063d7aa
2022-07-04 11:35:36 +02:00
James E. Blair 78953e9580 Fix race is test_tenant_add_remove
Change I101fc42e29f4b376272c417ec536c17f05cb1a60 introduced a race
in test_tenant_add_remove.  If the test takes longer than 1 second,
it will succeed, but less than the 1 second cache interval and it
will fail.

Correct that by disabling caching for the test.

Change-Id: I313e519ab26cdbdad0cc17cd4d3489b49482f0b3
2022-07-02 11:14:21 -07:00
James E. Blair 595fb3e9ef Add ssh_server option to Gerrit driver
In a multi-host Gerrit environment (HA or failover) it's plausible
that admins may use one mechanism for managing ingress for HTTP
requests and a different for SSH requests.  Or admins may have
different firewall rules for each. To accomodate these situations,
Add an "ssh_server" configuration item for Gerrit.  This makes the
set of hostname-like items the following:

* server: the HTTP hostname and default for all others
* canonical_hostname: what to use for golang-style git paths
* ssh_server: the hostname to use for SSH connections
* baseurl: the base URL for HTTP connections

The following are equivalent:
  server=review.example.com
  ssh_server=ssh-review.example.com
and:
  server=ssh-review.example.com
  baseurl=https://review.example.com

Change-Id: I6e9cd9f48c1a78d8d24bfe176efbb932a18ec83c
2022-06-22 14:10:41 +00:00