Browse Source

Run a gerrit container on review-dev01

This runs gerrit in a container on review-dev01 using podman.

Remove an unused web_server.py file that we found from copying it
from puppet to ansible.

Change-Id: I399d3cf8471bc8063022b0db0ff81718b2ee2941
changes/06/630406/69
James E. Blair 3 years ago committed by Monty Taylor
parent
commit
4f9720e76e
  1. 28
      .zuul.yaml
  2. 178
      modules/openstack_project/files/gerrit/web_server.py
  3. 1
      playbooks/roles/gerrit/README.rst
  4. 5
      playbooks/roles/gerrit/defaults/main.yaml
  5. 259
      playbooks/roles/gerrit/files/cla.html
  6. 179
      playbooks/roles/gerrit/files/etc/GerritSite.css
  7. 6
      playbooks/roles/gerrit/files/etc/GerritSiteHeader.html
  8. 16
      playbooks/roles/gerrit/files/gerrit-podman/docker-compose.yaml
  9. 4
      playbooks/roles/gerrit/files/hooks/change-abandoned
  10. 4
      playbooks/roles/gerrit/files/hooks/change-merged
  11. 11
      playbooks/roles/gerrit/files/robots.txt
  12. 575
      playbooks/roles/gerrit/files/static/hideci.js
  13. 14
      playbooks/roles/gerrit/files/static/system-cla.html
  14. 16
      playbooks/roles/gerrit/files/static/usg-cla.html
  15. 170
      playbooks/roles/gerrit/tasks/main.yaml
  16. 180
      playbooks/roles/gerrit/templates/gerrit.config
  17. 11
      playbooks/roles/gerrit/templates/patchset-created.j2
  18. 1
      playbooks/roles/install-podman/README.rst
  19. 29
      playbooks/roles/install-podman/defaults/main.yaml
  20. 20
      playbooks/roles/install-podman/tasks/main.yaml
  21. 1
      playbooks/roles/install-podman/templates/sources.list.j2
  22. 9
      playbooks/service-review-dev.yaml
  23. 27
      playbooks/zuul/run-base-post.yaml
  24. 2
      playbooks/zuul/run-base.yaml
  25. 34
      playbooks/zuul/templates/group_vars/review-dev.yaml.j2
  26. 29
      playbooks/zuul/templates/group_vars/review.yaml.j2
  27. 21
      testinfra/test_gerrit.py

28
.zuul.yaml

@ -1007,6 +1007,32 @@
- playbooks/roles/zuul-preview/
- testinfra/test_zuul_preview.py
- job:
name: system-config-run-review-dev
parent: system-config-run
description: |
Run the playbook for gerrit (in a container).
nodeset:
nodes:
- name: bridge.openstack.org
label: ubuntu-bionic
- name: review-dev01.openstack.org
label: ubuntu-bionic
vars:
run_playbooks:
- playbooks/service-review-dev.yaml
host-vars:
review-dev01.openstack.org:
host_copy_output:
'/home/gerrit2/review_site/etc': logs
'/home/gerrit2/review_site/logs': logs
files:
- playbooks/group_vars/review-dev.yaml
- ^playbooks/host_vars/review-dev\d+.opendev.org.yaml
- playbooks/zuul/templates/group_vars/review.yaml.j2
- playbooks/roles/gerrit/
- testinfra/test_gerrit.py
- job:
name: infra-prod-playbook
description: |
@ -1064,6 +1090,7 @@
soft: true
- name: system-config-build-image-haproxy-statsd
soft: true
- system-config-run-review-dev
- system-config-run-zuul-preview
- system-config-run-letsencrypt
- system-config-build-image-bazel
@ -1123,6 +1150,7 @@
soft: true
- name: system-config-upload-image-haproxy-statsd
soft: true
- system-config-run-review-dev
- system-config-run-zuul-preview
- system-config-run-letsencrypt
- system-config-upload-image-bazel

178
modules/openstack_project/files/gerrit/web_server.py

@ -1,178 +0,0 @@
#!/usr/bin/env python
#
# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
This is a simple test server that serves up the web content locally
as if it was a working remote server. It also proxies all the live
date/*.json files into the local test server, so that the Ajax async
loading works without hitting Cross Site Scripting violations.
"""
import argparse
import BaseHTTPServer
import os.path
import urllib2
import requests
# Values for these set via cli defaults
GERRIT_UPSTREAM = ""
ZUUL_UPSTREAM = ""
def replace_urls(line, port):
line = line.replace(
GERRIT_UPSTREAM,
"http://localhost:%s" % port)
line = line.replace(
ZUUL_UPSTREAM,
"http://localhost:%s" % port)
return line
class GerritHandler(BaseHTTPServer.BaseHTTPRequestHandler):
"""A request handler to create a magic local Gerrit server"""
def do_POST(self):
data = self.rfile.read(int(self.headers['content-length']))
headers = {}
# we need to trim some of the local headers in order for this
# request to remain valid.
for header in self.headers:
if header not in ("host", "origin", "connection"):
headers[header] = self.headers[header]
resp = requests.post("%s%s" %
(GERRIT_UPSTREAM, self.path),
headers=headers,
data=data)
# Process request back to client
self.send_response(resp.status_code)
for header in resp.headers:
# Requests has now decoded the response so it's no longer
# a gzip stream, which also means content-length is
# wrong. So we remove content-encoding, then drop
# content-length because if provided Gerrit strictly uses
# it for reads. We also drop all the keep-alive related
# headers, our server doesn't do that.
if header not in ("connection", "content-length",
"keep-alive", "content-encoding"):
self.send_header(header, resp.headers[header])
self.end_headers()
self.wfile.write(resp.text)
def do_GET(self):
# possible local file path
local_path = self.path.replace('/static/', '').split('?')[0]
# if the file exists locally, we'll serve it up directly
if os.path.isfile(local_path):
self.send_response(200, "Success")
self.end_headers()
with open(local_path) as f:
for line in f.readlines():
line = replace_urls(line, self.server.server_port)
self.wfile.write(line)
print "Loaded from local override"
return
# First we'll look for a zuul status call, /status doesn't map
# to gerrit so we can overload the localhost server for this.
if self.path.startswith("/status"):
try:
zuul_url = "%s%s" % (ZUUL_UPSTREAM, self.path)
# BUG(sdague): for some reason SSL connections to zuul
# from python 2.7 blow up with an SSL exception
zuul_url = zuul_url.replace('https', 'http')
response = urllib2.urlopen(zuul_url)
self.send_response(200, "Success")
for header in response.info():
# need to reset content-length otherwise jquery complains
if header not in ("connection", "content-length",
"keep-alive", "content-encoding"):
self.send_header(header, response.info()[header])
self.end_headers()
for line in response.readlines():
line = replace_urls(line, self.server.server_port)
self.wfile.write(line)
return
except urllib2.HTTPError as e:
self.send_response(e.code)
self.end_headers()
self.wfile.write(e.read())
return
except urllib2.URLError as e:
print "URLError on %s" % (zuul_url)
print e
# If you've not built local data to test with, instead grab
# the data off the production server on the fly and serve it
# up from our server.
try:
response = urllib2.urlopen("%s%s" %
(GERRIT_UPSTREAM, self.path))
self.send_response(200, "Success")
for header in response.info():
self.send_header(header, response.info()[header])
self.end_headers()
for line in response.readlines():
line = replace_urls(line, self.server.server_port)
self.wfile.write(line)
except urllib2.HTTPError as e:
self.send_response(e.code)
self.end_headers()
self.wfile.write(e.read())
def parse_opts():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('-p', '--port',
help='port to bind to [default: 8001]',
type=int,
default=8001)
parser.add_argument('-z', '--zuul-url',
help='url for zuul server',
default="https://zuul.openstack.org")
parser.add_argument('-g', '--gerrit-url',
help='url for gerrit server',
default="https://review.opendev.org")
return parser.parse_args()
def main():
global ZUUL_UPSTREAM
global GERRIT_UPSTREAM
opts = parse_opts()
ZUUL_UPSTREAM = opts.zuul_url
GERRIT_UPSTREAM = opts.gerrit_url
server_address = ('', opts.port)
httpd = BaseHTTPServer.HTTPServer(server_address, GerritHandler)
print "Test Server is running at http://localhost:%s" % opts.port
print "Ctrl-C to exit"
print
while True:
httpd.handle_request()
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print "\n"
print "Thanks for testing! Please come again."

1
playbooks/roles/gerrit/README.rst

@ -0,0 +1 @@
Run Gerrit.

5
playbooks/roles/gerrit/defaults/main.yaml

@ -0,0 +1,5 @@
gerrit_id: 3000
gerrit_user_name: gerrit2
gerrit_home_dir: /home/gerrit2
gerrit_site_dir: "{{ gerrit_home_dir }}/review_site"
gerrit_run_init: false

259
playbooks/roles/gerrit/files/cla.html

@ -0,0 +1,259 @@
<html><body><div>
<h1>
OpenStack Project Individual Contributor License Agreement
</h1>
<!--
This is the current OpenStack Project Individual Contributor License Agreement
reformatted for HTML from the original "RevisedCLA.doc" with SHA1 sum
0467dd893d276cefde614e063a363b995d67e5ee provided by Jonathan Bryce, Executive
Director on behalf of The OpenStack Foundation on Monday, January 7, 2013. No
textual content was changed except to replace quote marks with their strict
ASCII equivalents, add the original LLC CLA text as a block quote, and restore
a previously inapplicable statement (sentence #2 of paragraph #2) adapted from
the Apache Software Foundation ICLA at his direction.
-->
<p><em>
In order to clarify the intellectual property license granted with
Contributions from any person or entity, the OpenStack Project (the "Project")
must have a Contributor License Agreement ("Agreement") on file that has been
signed by each Contributor, indicating agreement to the license terms below.
This license is for your protection as a Contributor as well as the protection
of OpenStack Foundation as Project manager (the "Project Manager") and the
Project users; it does not change your rights to use your own Contributions for
any other purpose.
</em></p>
<p><em>
You accept and agree to the following terms and conditions for Your present and
future Contributions submitted to the Project Manager. In return, the Project
Manager shall not use Your Contributions in a way that is contrary to the
public benefit or inconsistent with its nonprofit status and bylaws in effect
at the time of the Contribution. Except for the license granted herein to the
Project Manager and recipients of software distributed by the Project Manager,
You reserve all right, title, and interest in and to Your Contributions.
</em></p>
<ol>
<li><p><strong>
Definitions.
</strong>
"You" (or "Your") shall mean the copyright owner or legal entity authorized by
the copyright owner that is making this Agreement with the Project Manager. For
legal entities, the entity making a Contribution and all other entities that
control, are controlled by, or are under common control with that entity are
considered to be a single Contributor. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the direction or
management of such entity, whether by contract or otherwise, or (ii) ownership
of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial
ownership of such entity. "Contribution" shall mean any original work of
authorship, including any modifications or additions to an existing work, that
is intentionally submitted by You to the Project Manager for inclusion in, or
documentation of, any of the projects owned or managed by the Project Manager
(the "Work"). For the purposes of this definition, "submitted" means any form of
electronic, verbal, or written communication sent to the Project Manager or its
representatives, including but not limited to communication on electronic
mailing lists, source code control systems, and issue tracking systems that are
managed by, or on behalf of, the Project Manager for the purpose of discussing
and improving the Work, but excluding communication that is conspicuously marked
or otherwise designated in writing by You as "Not a Contribution."
</p></li>
<li><p><strong>
Grant of Copyright License.
</strong>
Subject to the terms and conditions of this Agreement, You hereby grant to the
Project Manager and to recipients of software distributed by the Project
Manager a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare derivative works of,
publicly display, publicly perform, sublicense, and distribute Your
Contributions and such derivative works.
</p></li>
<li><p><strong>
Grant of Patent License.
</strong>
Subject to the terms and conditions of this Agreement, You hereby grant to the
Project Manager and to recipients of software distributed by the Project
Manager a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by You that are
necessarily infringed by Your Contribution(s) alone or by combination of Your
Contribution(s) with the Work to which such Contribution(s) was submitted. If
any entity institutes patent litigation against You or any other entity
(including a cross-claim or counterclaim in a lawsuit) alleging that Your
Contribution, or the Work to which You have contributed, constitutes direct or
contributory patent infringement, then any patent licenses granted to that
entity under this Agreement for that Contribution or Work shall terminate as of
the date such litigation is filed.
</p></li>
<li><p>
You represent that you are legally entitled to grant the above license. If your
employer(s) has rights to intellectual property that you create that includes
your Contributions, You represent that you have received permission to make
Contributions on behalf of that employer, that your employer has waived such
rights for your Contributions to the Project Manager, or that your employer has
executed a separate Corporate Contributor License Agreement with the Project
Manager.
</p></li>
<li><p>
You represent that each of Your Contributions is Your original creation (see
Section 7 for submissions on behalf of others). You represent that Your
Contribution submissions include complete details of any third-party license or
other restriction (including, but not limited to, related patents and
trademarks) of which you are personally aware and which are associated with any
part of Your Contributions.
</p></li>
<li><p>
You are not expected to provide support for Your Contributions, except to the
extent You desire to provide support. You may provide support for free, for a
fee, or not at all. Unless required by applicable law or agreed to in writing,
You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied, including, without
limitation, any warranties or conditions of TITLE, NONINFRINGEMENT,
MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
</p></li>
<li><p>
Should You wish to submit work that is not Your original creation, You may
submit it to the Project Manager separately from any Contribution, identifying
the complete details of its source and of any license or other restriction
(including, but not limited to, related patents, trademarks, and license
agreements) of which you are personally aware, and conspicuously marking the
work as "Submitted on behalf of a third-party: [named here]".
</p></li>
<li><p>
You agree to notify the Project Manager of any facts or circumstances of which
you become aware that would make these representations inaccurate in any
respect.
</p></li>
<li><p>
In addition, if you have provided a Contribution (as defined in the LLC
Contribution License Agreement below) to the Project under the Contribution
License Agreement to OpenStack, LLC ("LLC Contribution Agreement"), you agree
that OpenStack, LLC may assign the LLC Contribution Agreement along with all
its rights and obligations under the LLC Contribution License Agreement to the
Project Manager.
</p></li>
</ol>
<blockquote>
<p><em>In order to clarify the intellectual property license granted with
Contributions from any person or entity, the OpenStack Project (the "Project")
must have a Contributor License Agreement ("Agreement") on file that has been
signed by each Contributor, indicating agreement to the license terms below.
This license is for your protection as a Contributor as well as the protection
of OpenStack, LLC as Project manager (the "Project Manager") and the Project
users; it does not change your rights to use your own Contributions for any
other purpose. If you have not already done so, please complete and sign this
Individual License Agreement by following the instructions embedded below.
After you fill in the required information and apply your digital signature to
the Agreement, the signature service will generate an email to you. You must
confirm your digital signature as instructed in this email to complete the
signing process. The signature service will then send you a signed copy of this
Agreement for your records.</em></p>
<p><em>You accept and agree to the following terms and conditions for Your
present and future Contributions submitted to the Project Manager. Except for
the license granted herein to the Project Manager and recipients of software
distributed by the Project Manager, You reserve all right, title, and interest
in and to Your Contributions.</em></p>
<ol>
<li><p><strong>Definitions</strong>"You" (or "Your") shall mean the copyright
owner or legal entity authorized by the copyright owner that is making this
Agreement with the Project Manager. For legal entities, the entity making a
Contribution and all other entities that control, are controlled by, or are
under common control with that entity are considered to be a single
Contributor. For the purposes of this definition, "control" means (i) the
power, direct or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or
more of the outstanding shares, or (iii) beneficial ownership of such
entity. "Contribution" shall mean any original work of authorship, including
any modifications or additions to an existing work, that is intentionally
submitted by You to the Project Manager for inclusion in, or documentation of,
any of the projects owned or managed by the Project Manager (the "Work"). For
the purposes of this definition, "submitted" means any form of electronic,
verbal, or written communication sent to the Project Manager or its
representatives, including but not limited to communication on electronic
mailing lists, source code control systems, and issue tracking systems that are
managed by, or on behalf of, the Project Manager for the purpose of discussing
and improving the Work, but excluding communication that is conspicuously
marked or otherwise designated in writing by You as "Not a
Contribution."</p></li>
<li><p><strong>Grant of Copyright License.</strong> Subject to the terms and
conditions of this Agreement, You hereby grant to the Project Manager and to
recipients of software distributed by the Project Manager a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright
license to reproduce, prepare derivative works of, publicly display, publicly
perform, sublicense, and distribute Your Contributions and such derivative
works.</p></li>
<li><p><strong>Grant of Patent License.</strong> Subject to the terms and
conditions of this Agreement, You hereby grant to the Project Manager and to
recipients of software distributed by the Project Manager a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as
stated in this section) patent license to make, have made, use, offer to sell,
sell, import, and otherwise transfer the Work, where such license applies only
to those patent claims licensable by You that are necessarily infringed by Your
Contribution(s) alone or by combination of Your Contribution(s) with the Work
to which such Contribution(s) was submitted. If any entity institutes patent
litigation against You or any other entity (including a cross-claim or
counterclaim in a lawsuit) alleging that Your Contribution, or the Work to
which You have contributed, constitutes direct or contributory patent
infringement, then any patent licenses granted to that entity under this
Agreement for that Contribution or Work shall terminate as of the date such
litigation is filed.</p></li>
<li><p>You represent that you are legally entitled to grant the above license.
If your employer(s) has rights to intellectual property that you create that
includes your Contributions, You represent that you have received permission to
make Contributions on behalf of that employer, that your employer has waived
such rights for your Contributions to the Project Manager, or that your
employer has executed a separate Corporate Contributor License Agreement with
the Project Manager.</p></li>
<li><p>You represent that each of Your Contributions is Your original creation
(see Section 7 for submissions on behalf other others). You represent that Your
Contribution submissions include complete details of any third-party license or
other restriction (including, but not limited to, related patents and
trademarks) of which you are personally aware and which are associated with any
part of Your Contributions.</p></li>
<li><p>You are not expected to provide support for Your Contributions, except
to the extent You desire to provide support. You may provide support for free,
for a fee, or not at all. Unless required by applicable law or agreed to in
writing, You provide Your Contributions on as "AS IS" BASIS, WITHOUT WARRANTIES
OR CONDITIONS OR ANY KIND, either express or implied, including, without
limitation, any warranties or conditions of TITLE, NONINFRINGEMENT,
MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.</p></li>
<li><p>Should You wish to submit work that is not Your original creation, You
may submit it to the Project Manager separately from any Contribution,
identifying the complete details of its source and of any license or other
restriction (including, but not limited to, related patents, trademarks, and
license agreements) of which you are personally aware, and conspicuously
marking the work as "Submitted on behalf of a third-party: [named
here]".</p></li>
<li><p>You agree to notify the Project Manager of any facts or circumstances of
which you become aware that would make these representations inaccurate in any
respect.</p></li>
</ol>
</blockquote>
</div></body></html>

179
playbooks/roles/gerrit/files/etc/GerritSite.css

@ -0,0 +1,179 @@
body {color: #000 !important;}
a,a:visited {color: #264D69 !important; text-decoration: none;}
a:hover {color: #000 !important; text-decoration: underline}
a.gwt-InlineHyperlink {background: none !important}
#openstack-logo img { height: 40px; }
#openstack-logo h1 { margin: 20px; }
#gerrit_header #openstack-logo h1 { margin: 20px 0 0 0; }
#gerrit_header {display: block !important; position: relative; top: -60px; margin-bottom: -60px; width: 200px; padding-left: 17px}
#gerrit_header h1 {font-family: 'PT Sans', sans-serif; font-weight: normal; letter-spacing: -1px}
#gerrit_topmenu {background: none; position:relative; top: 0px; left: 220px; margin-right: 220px}
#gerrit_topmenu tbody tr td table {border: 0}
#gerrit_topmenu tbody tr td table.gwt-TabBar {color: #353535; border-bottom: 1px solid #C5E2EA;}
#gerrit_topmenu .gwt-Button {padding: 3px 6px}
.gwt-TabBarItem-selected {color: #CF2F19 !important; border-bottom: 3px solid #CF2F19;}
.gwt-TabBarItem {color: #353535; border-right: 0 !important}
.gwt-TabBar .gwt-TabBarItem, .gwt-TabBar .gwt-TabBarRest, .gwt-TabPanelBottom {background: 0 !important;}
#gerrit_topmenu .searchTextBox {width: 250px}
#change_infoTable {
border-collapse: collapse;
}
#change_infoTable th {
padding: 2px 4px 2px 6px;
background-color: #eef3f5;
font-style: italic;
text-align: left;
}
#change_infoTable td {
padding: 2px 4px 2px 6px;
border-bottom: 1px solid #eef3f5;
border-right: 1px solid #eef3f5;
}
#change_infoTable tr:last-child td {
border: none;
}
.com-google-gerrit-client-change-ChangeScreen_BinderImpl_GenCss_style-cs2 {
padding-left: 10px;
padding-right: 10px;
}
/* Section headers */
.com-google-gerrit-client-change-ChangeScreen_BinderImpl_GenCss_style-cs2
.com-google-gerrit-client-change-ChangeScreen_BinderImpl_GenCss_style-headerLine,
.com-google-gerrit-client-change-ChangeScreen_BinderImpl_GenCss_style-cs2
.com-google-gerrit-client-change-ChangeScreen_BinderImpl_GenCss_style-sectionHeader {
margin-top: 10px !important;
margin-bottom: 10px !important;
}
/* Commit message */
.com-google-gerrit-client-change-ChangeScreen_BinderImpl_GenCss_style-cs2
.com-google-gerrit-client-change-CommitBox_BinderImpl_GenCss_style-text::first-line {
font-weight: bold !important;
}
/* Commit metadata */
.com-google-gerrit-client-change-ChangeScreen_BinderImpl_GenCss_style-cs2
.com-google-gerrit-client-change-ChangeScreen_BinderImpl_GenCss_style-commitColumn
.com-google-gerrit-client-change-CommitBox_BinderImpl_GenCss_style-header th {
padding: 2px 4px 2px 6px;
background-color: #eef3f5;
font-style: italic;
text-align: left;
}
.com-google-gerrit-client-change-ChangeScreen_BinderImpl_GenCss_style-cs2
.com-google-gerrit-client-change-ChangeScreen_BinderImpl_GenCss_style-commitColumn
.com-google-gerrit-client-change-CommitBox_BinderImpl_GenCss_style-header td {
border-bottom: 1px solid #eef3f5;
padding: 2px 4px 2px 6px;
}
.com-google-gerrit-client-change-ChangeScreen_BinderImpl_GenCss_style-cs2
.com-google-gerrit-client-change-ChangeScreen_BinderImpl_GenCss_style-commitColumn
.com-google-gerrit-client-change-CommitBox_BinderImpl_GenCss_style-header td:last-child {
border-right: 1px solid #eef3f5;
}
/* increase the middle info column to fill empty space (for wide
monitors), but ensure there is a sufficient lower bound to kick in
horiz scroll bar. This will relieve the preasure on the hideci test
results. */
.com-google-gerrit-client-change-ChangeScreen_BinderImpl_GenCss_style-infoColumn {
width: 100% !important;
min-width: 400px;
}
/* Review history */
.com-google-gerrit-client-change-ChangeScreen_BinderImpl_GenCss_style-cs2
.com-google-gerrit-client-change-Message_BinderImpl_GenCss_style-name {
font-weight: bold !important;
}
.com-google-gerrit-client-change-ChangeScreen_BinderImpl_GenCss_style-cs2
.com-google-gerrit-client-change-Message_BinderImpl_GenCss_style-messageBox {
width: inherit;
max-width: 1168px;
}
.comment_test_name {
display: inline-block;
*display: inline;
*zoom: 1;
width: auto !important;
width: 25em;
min-width: 20em;
padding-bottom: 2pt;
}
.comment_test_result {
}
.result_SUCCESS {
color: #007f00;
}
.result_FAILURE, .result_POST_FAILURE, .result_TIMED_OUT, .result_RETRY_LIMIT, .result_DISK_FULL {
color: #cf2f19;
}
.result_UNSTABLE, .result_WARNING {
color: #e39f00;
}
.result_LOST {
color: #e39f00;
}
li.comment_test {list-style-type: none; }
/* this is for support of 'Display Person Name In Review Category' */
.cAPPROVAL {
max-width: 100px;
overflow: hidden;
}
/* fixes to make this like old gerrit */
.changeTable td.dataCell {
height: 1em;
}
/* don't make the non voting stand out more than the voting */
table.infoTable td.notVotable,
.changeTable td.dataCell.labelNotApplicable {
background: inherit;
}
.test_result_table {
border-collapse: collapse;
}
.test_result_table tr {
border-left: 1px solid #eef3f5;
border-right: 1px solid #eef3f5;
}
.test_result_table td.header {
background-color: #eef3f5;
}
.test_result_table td {
padding: 2px 4px 2px 6px;
border: 1px solid #eef3f5;
}
.addMemberTextBox {
width: 20em;
}
/* css attribute selector to make -1s show up red in new screen */
[title="Doesn't seem to work"],
[title="This patch needs further work before it can be merged"] {
color: red;
}

6
playbooks/roles/gerrit/files/etc/GerritSiteHeader.html

@ -0,0 +1,6 @@
<div id="openstack-logo">
<script type="text/javascript" src="static/jquery.js" />
<script type="text/javascript" src="static/jquery-visibility.js"></script>
<script type="text/javascript" src="static/hideci.js" />
<a href="/"><h1><img src="static/title.svg" style="vertical-align:middle;" /></h1></a>
</div>

16
playbooks/roles/gerrit/files/gerrit-podman/docker-compose.yaml

@ -0,0 +1,16 @@
version: '3'
services:
gerrit:
image: docker.io/opendevorg/gerrit:2.13
network_mode: host
user: gerrit
volumes:
- /home/gerrit2/review_site/cache:/var/gerrit/cache
- /home/gerrit2/review_site/etc:/var/gerrit/etc
- /home/gerrit2/review_site/git:/var/gerrit/git
- /home/gerrit2/review_site/tmp:/var/gerrit/tmp
- /home/gerrit2/review_site/hooks:/var/gerrit/hooks
- /home/gerrit2/review_site/index:/var/gerrit/index
- /home/gerrit2/review_site/logs:/var/log/gerrit
- /home/gerrit2/review_site/static:/var/gerrit/static

4
playbooks/roles/gerrit/files/hooks/change-abandoned

@ -0,0 +1,4 @@
#!/bin/sh
# Use timeout to kill any process running longer than 10 minutes.
timeout -k 2m 10m /usr/local/bin/update-bug change-abandoned "$@"

4
playbooks/roles/gerrit/files/hooks/change-merged

@ -0,0 +1,4 @@
#!/bin/sh
# Use timeout to kill any process running longer than 10 minutes.
timeout -k 2m 10m /usr/local/bin/update-bug change-merged "$@"

11
playbooks/roles/gerrit/files/robots.txt

@ -0,0 +1,11 @@
# Directions for web crawlers.
# See http://www.robotstxt.org/wc/norobots.html.
User-agent: HTTrack
User-agent: puf
User-agent: MSIECrawler
User-agent: Nutch
Disallow: /
User-agent: msnbot
Crawl-delay: 1

575
playbooks/roles/gerrit/files/static/hideci.js

@ -0,0 +1,575 @@
// Copyright (c) 2014 VMware, Inc.
// Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// this regex matches the hash part of review pages
var hashRegex = /^\#\/c\/([\d]+)((\/\d+)([.][.](\d+))?)?\/?$/;
// this regex matches CI comments
var ciRegex = /^(.* CI|Jenkins|Zuul)$/;
// this regex matches "Patch set #"
var psRegex = /^(Uploaded patch set|Patch Set) (\d+)(:|\.)/;
// this regex matches merge failure messages
var mergeFailedRegex = /Merge Failed\./;
// this regex matches the name of CI systems we trust to report merge failures
var trustedCIRegex = /^(OpenStack CI|Jenkins|Zuul)$/;
// this regex matches the name+pipeline that we want at the top of the CI list
var firstPartyCI = /^(Jenkins|Zuul)/;
// this regex matches the pipeline markup
var pipelineNameRegex = /Build \w+ \(([-\w]+) pipeline\)/;
// The url to full status information on running jobs
var zuulStatusURL = 'https://zuul.openstack.org';
// The json URL to check for running jobs
var zuulStatusJSON = 'https://zuul.openstack.org/status/change/';
// This is a variable to determine if we're in debugging mode, which
// lets you globally set it to see what's going on in the flow.
var hideci_debug = false;
// This is a variable to enable zuul integration, we default it off so
// that it creates no additional load, and that it's easy to turn off
// the feature.
var zuul_inline = false;
/**
dbg(...) - prints a list of items out to the javascript
console.log. This allows us to leave tracing in this file which is a
no-op by default, but can be triggered if you enter a javascript
console and set hideci_debug = true.
*/
function dbg () {
if (hideci_debug == true) {
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
}
function format_time(ms, words) {
if (ms == null) {
return "unknown";
}
var seconds = (+ms)/1000;
var minutes = Math.floor(seconds/60);
var hours = Math.floor(minutes/60);
seconds = Math.floor(seconds % 60);
minutes = Math.floor(minutes % 60);
r = '';
if (words) {
if (hours) {
r += hours;
r += ' hr ';
}
r += minutes + ' min';
} else {
if (hours < 10) r += '0';
r += hours + ':';
if (minutes < 10) r += '0';
r += minutes + ':';
if (seconds < 10) r += '0';
r += seconds;
}
return r;
}
var ci_parse_psnum = function($panel) {
var match = psRegex.exec($panel.html());
if (match !== null) {
return parseInt(match[2]);
}
return 0;
};
var ci_parse_is_merge_conflict = function($panel) {
return (mergeFailedRegex.exec($panel.html()) !== null);
};
var ci_find_pipeline = function($panel) {
var match = pipelineNameRegex.exec($panel.html());
if (match !== null) {
return match[1];
} else {
return null;
}
};
var ci_parse_results = function($panel) {
var result_list = [];
var test_results = $panel.find("li.comment_test");
var pipeline = null;
if (test_results !== null) {
test_results.each(function(i, li) {
var result = {};
if ($(li).find("a").length > 0) {
result["name"] = $(li).find("span.comment_test_name").find("a")[0].innerHTML;
result["link"] = $(li).find("span.comment_test_name").find("a")[0];
}
else {
result["name"] = $(li).find("span.comment_test_name")[0].innerHTML;
}
result["result"] = $(li).find("span.comment_test_result")[0];
result_list.push(result);
});
}
return result_list;
};
/***
* function ci_group_by_ci_pipeline - create a group by structure for
* iterating on CI's pipelines
*
* This function takes the full list of comments, the current patch
* number, and builds an array of (ci_name_pipelinename, comments array)
* tuples. That makes it very easy to process during the display
* phase to ensure we only display the latest result for every CI
* pipeline.
*
* Comments that do not have a parsable pipeline (3rd party ci
* systems) get collapsed by name, and we specify 'check' for their
* pipeline.
*
**/
var ci_group_by_ci_pipeline = function(current, comments) {
var ci_pipelines = [];
var ci_pipeline_comments = [];
for (var i = 0; i < comments.length; i++) {
var comment = comments[i];
if ((comment.psnum != current) || !comment.is_ci || (comment.results.length == 0)) {
continue;
}
var name_pipeline = comment.name;
if (comment.pipeline !== null) {
name_pipeline += ' ' + comment.pipeline;
}
var index = ci_pipelines.indexOf(name_pipeline);
if (index == -1) {
// not found, so create new entries
ci_pipelines.push(name_pipeline);
ci_pipeline_comments.push([comment]);
} else {
ci_pipeline_comments[index].push(comment);
}
}
function sort_by_name(a,b) {
if (a[0] < b[0])
return -1;
else if (a[0] > b[0])
return 1;
else
return 0;
}
var results = [];
var notfirstparty = [];
// we want to separate out first party CI results to always be the
// top of the list, and third party CI to come after, so that
// hunting for first party CI isn't tough.
for (i = 0; i < ci_pipelines.length; i++) {
if (firstPartyCI.test(ci_pipelines[i])) {
results.push([ci_pipelines[i], ci_pipeline_comments[i]]);
} else {
notfirstparty.push([ci_pipelines[i], ci_pipeline_comments[i]]);
}
}
notfirstparty.sort(sort_by_name);
for (i = 0; i < notfirstparty.length; i++) {
results.push(notfirstparty[i]);
}
return results;
};
var ci_parse_comments = function() {
var comments = [];
$("p").each(function() {
var match = psRegex.exec($(this).html());
if (match !== null) {
var psnum = parseInt(match[2]);
var top = $(this).parent().parent().parent();
// old change screen
var name = top.attr("name");
if (!name) {
// new change screen
name = $(this).parent().parent().parent().children().children()[0].innerHTML;
top = $(this).parent().parent().parent().parent();
}
var comment = {};
comment.name = name;
var date_cell = top.find(".commentPanelDateCell");
if (date_cell.attr("title")) {
// old change screen
comment.date = date_cell.attr("title");
} else {
// new change screen
comment.date = $(this).parent().parent().parent().children().children()[2].innerHTML
}
var comment_panel = $(this).parent();
comment.psnum = psnum;
comment.merge_conflict = ci_parse_is_merge_conflict(comment_panel);
comment.pipeline = ci_find_pipeline(comment_panel);
comment.results = ci_parse_results(comment_panel);
comment.is_ci = (ciRegex.exec(comment.name) !== null);
comment.is_trusted_ci = (trustedCIRegex.exec(comment.name) !== null);
comment.ref = top;
dbg("Found comment", comment);
comments.push(comment);
}
});
return comments;
};
var ci_latest_patchset = function(comments) {
var psnum = 0;
for (var i = 0; i < comments.length; i++) {
psnum = Math.max(psnum, comments[i].psnum);
}
return psnum;
};
var ci_is_merge_conflict = function(comments) {
var latest = ci_latest_patchset(comments);
var conflict = false;
for (var i = 0; i < comments.length; i++) {
var comment = comments[i];
// only if we are actually talking about the latest patch set
if (comment.psnum == latest) {
if (comment.is_trusted_ci) {
conflict = comment.merge_conflict;
}
}
}
return conflict;
};
var ci_prepare_results_table = function() {
// Create a table and insert it after the approval table
var table = $("table.test_result_table")[0];
if (!table) {
table = document.createElement("table");
$(table).addClass("test_result_table");
$(table).addClass("infoTable").css({"margin-top":"1em", "margin-bottom":"1em"});
var approval_table = $("div.approvalTable");
if (approval_table.length) {
var outer_table = document.createElement("table");
$(outer_table).insertBefore(approval_table);
var outer_table_row = document.createElement("tr");
$(outer_table).append(outer_table_row);
var td = document.createElement("td");
$(outer_table_row).append(td);
$(td).css({"vertical-align":"top"});
$(td).append(approval_table);
td = document.createElement("td");
$(outer_table_row).append(td);
$(td).css({"vertical-align":"top"});
$(td).append(table);
} else {
var big_table_row = $("div.screen>div>div>table>tbody>tr");
var td = $(big_table_row).children()[1];
$(td).append(table);
}
} else {
$(table).empty();
}
return table;
};
var ci_display_results = function(comments) {
var table = ci_prepare_results_table();
if (ci_is_merge_conflict(comments)) {
var mc_header = $("<tr>").append($('<td class="merge_conflict" colpsan="2">Patch in Merge Conflict</td>'));
mc_header.css('width', '400');
mc_header.css('font-weight', 'bold');
mc_header.css('color', 'red');
mc_header.css('padding-left', '2em');
$(table).append(mc_header);
return;
}
var current = ci_latest_patchset(comments);
var ci_pipelines = ci_group_by_ci_pipeline(current, comments);
for (var i = 0; i < ci_pipelines.length; i++) {
var ci_pipeline_name = ci_pipelines[i][0];
var ci_pipeline_comments = ci_pipelines[i][1];
// the most recent comment on a pipeline
var last = ci_pipeline_comments.length - 1;
var comment = ci_pipeline_comments[last];
var rechecks = "";
if (last > 0) {
rechecks = " (" + last + " rechecks)";
}
var header = $("<tr>").append($('<td class="header">' + ci_pipeline_name + rechecks + '</td>'));
header.append('<td class="header ci_date">' + comment.date + '</td>');
$(table).append(header);
for (var j = 0; j < comment.results.length; j++) {
var result = comment.results[j];
var tr = $("<tr>");
if ("link" in result) {
tr.append($("<td>").append($(result["link"]).clone()));
}
else {
tr.append($("<td>").text(result["name"]));
}
tr.append($("<td>").append($(result["result"]).clone()));
$(table).append(tr);
}
}
};
var set_cookie = function (name, value) {
document.cookie = name + "=" + value + "; path=/";
};
var read_cookie = function (name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1, c.length);
}
if (c.indexOf(nameEQ) == 0) {
return c.substring(nameEQ.length, c.length);
}
}
return null;
};
var ci_toggle_visibility = function(comments, showOrHide) {
if (!comments) {
comments = ci_parse_comments();
}
$.each(comments, function(i, comment) {
if (comment.is_ci && !comment.is_trusted_ci) {
$(comment.ref).toggle(showOrHide);
}
});
};
var ci_hide_ci_comments = function(comments) {
if (!comments) {
comments = ci_parse_comments();
}
$.each(comments, function(i, comment) {
if (comment.is_ci && !comment.is_trusted_ci) {
$(comment.ref).hide();
}
});
};
var ci_page_loaded = function() {
if (hashRegex.test(window.location.hash)) {
dbg("Searching for ci results on " + window.location.hash);
$("#toggleci").show();
var comments = ci_parse_comments();
ci_display_results(comments);
var showOrHide = 'true' == read_cookie('show-ci-comments');
if (!showOrHide) {
ci_hide_ci_comments(comments);
}
if (zuul_inline === true) {
ci_zuul_for_change(comments);
}
} else {
$("#toggleci").hide();
}
};
var ci_current_change = function() {
var change = hashRegex.exec(window.location.hash);
if (change.length > 1) {
return change[1];
}
return null;
};
// recursively find the zuul status change, will be much more
// efficient once zuul supports since json status.
var ci_find_zuul_status = function (data, change_psnum) {
var objects = [];
for (var i in data) {
if (!data.hasOwnProperty(i)) continue;
if (typeof data[i] == 'object') {
objects = objects.concat(ci_find_zuul_status(data[i],
change_psnum));
} else if (i == 'id' && data.id == change_psnum) {
objects.push(data);
}
}
return objects;
};
var ci_zuul_all_status = function(jobs) {
var status = "passing";
for (var i = 0; i < jobs.length; i++) {
if (jobs[i].result && jobs[i].result != "SUCCESS") {
status = "failing";
break;
}
}
return status;
};
var ci_zuul_display_status = function(status) {
var zuul_table = $("table.zuul_result_table")[0];
if (!zuul_table) {
var test_results = $("table.test_result_table")[0];
zuul_table = document.createElement("table");
$(zuul_table).addClass("zuul_result_table");
$(zuul_table).addClass("infoTable").css({"margin-bottom":"1em"});
if (test_results) {
$(test_results).prepend(zuul_table);
}
}
$(zuul_table).empty();
$(zuul_table).show();
$(zuul_table).append("<tr><td class='header'>Change currently being tested (<a href='" + zuulStatusURL + "'>full status</a>)</td></tr>");
for (var i = 0; i < status.length; i++) {
var item = status[i];
var pipeline = item.jobs[0].pipeline;
var passing = (item.failing_reasons && item.failing_reasons.length > 0) ? "failing" : "passing";
var timeleft = item.remaining_time;
var row = "<tr><td>";
if (pipeline != null) {
row += pipeline + " pipeline: " + passing;
row += " (" + format_time(timeleft, false) + " left)";
} else {
row += "in between pipelines, status should update shortly";
}
row += "</td></tr>";
$(zuul_table).append(row);
}
};
var ci_zuul_clear_status = function () {
var zuul_table = $("table.zuul_result_table")[0];
if (zuul_table) {
$(zuul_table).hide();
}
};
var ci_zuul_process_changes = function(data, change_psnum) {
var zuul_status = ci_find_zuul_status(data, change_psnum);
if (zuul_status.length) {
ci_zuul_display_status(zuul_status);
} else {
ci_zuul_clear_status();
}
};
var ci_zuul_for_change = function(comments) {
if (!comments) {
comments = ci_parse_comments();
}
var change = ci_current_change();
var psnum = ci_latest_patchset(comments);
var change_psnum = change + "," + psnum;
// do the loop recursively in ajax
(function poll() {
$.ajax({
url: zuulStatusJSON + change_psnum,
type: "GET",
success: function(data) {
dbg("Found zuul data for " + change_psnum, data);
ci_zuul_process_changes(data, change_psnum);
},
dataType: "json",
complete: setTimeout(function() {
// once we are done with this cycle in the loop we
// schedule ourselves again in the future with
// setTimeout. However, by the time the function
// actually gets called, other things might have
// happened, and we may want to just dump the data
// instead.
//
// the UI might have gone hidden (user was bored,
// switched to another tab / window).
//
// the user may have navigated to another review url,
// so the data returned is not relevant.
//
// both cases are recoverable when the user navigates
// around, because a new "thread" gets started on
// ci_page_load.
//
// BUG(sdague): there is the possibility that the user
// navigates away from a page and back fast enough
// that the first "thread" is not dead, and a second
// one is started. greghaynes totally said he'd come
// up with a way to fix that.
if (window.zuul_enable_status_updates == false) {
return;
}
var current = ci_current_change();
if (current && change_psnum.indexOf(current) != 0) {
// window url is dead, so don't schedule any more future
// updates for this url.
return;
}
poll();
}, 15000),
timeout: 5000
});
})();
};
window.onload = function() {
var input = document.createElement("input");
input.id = "toggleci";
input.type = "button";
input.className = "gwt-Button";
input.value = "Toggle Extra CI";
input.onclick = function () {
// Flip the cookie
var showOrHide = 'true' == read_cookie('show-ci-comments');
set_cookie('show-ci-comments', showOrHide ? 'false' : 'true');
// Hide or Show existing comments based on cookie
ci_toggle_visibility(null, !showOrHide);
};
document.body.appendChild(input);
MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
var observer = new MutationObserver(function(mutations, observer) {
var span = $("span.rpcStatus");
$.each(mutations, function(i, mutation) {
if (mutation.target === span[0] &&
mutation.attributeName === "style" &&
(!(span.is(":visible")))) {
ci_page_loaded();
}
});
});
observer.observe(document, {
subtree: true,
attributes: true
});
$(document).on({
'show.visibility': function() {
window.zuul_enable_status_updates = true;
ci_page_loaded();
},
'hide.visibility': function() {
window.zuul_enable_status_updates = false;
}
});
};

14
playbooks/roles/gerrit/files/static/system-cla.html

@ -0,0 +1,14 @@
<html><body><div>
<h1>
Gerrit System Role Accounts Proxy CLA
</h1>
<p>
This is not a real CLA and cannot be signed. See <a
href="https://docs.openstack.org/infra/manual/developers.html#account-setup">the
account setup instructions</a> for more information on OpenStack Contributor
License Agreements.
</p>
</div></body></html>

16
playbooks/roles/gerrit/files/static/usg-cla.html

@ -0,0 +1,16 @@
<html><body><div>
<h1>
OpenStack Project U.S. Government Contributor License Agreement
</h1>
<p>
This agreement is not managed through Gerrit. If you need to sign the U.S.
Government Contributor License Agreement, please contact the OpenStack
Foundation to initiate the process. See <a
href="https://docs.openstack.org/infra/manual/developers.html#account-setup">the
account setup instructions</a> for more information on OpenStack Contributor
License Agreements.
</p>
</div></body></html>

170
playbooks/roles/gerrit/tasks/main.yaml

@ -0,0 +1,170 @@
# TODO(mordred) We should do *something* where this could use a zuul cloned
# copy of project-config instead. This is needed not just for things like
# manage-projects (which could be run completely differently and non-locally)
# but also for things like notify-impact, which is currently run by a gerrit
# hook inside of the container via jeepyb.
- name: Clone project-config repo
git:
repo: https://opendev.org/openstack/project-config
dest: /opt/project-config
force: yes
- name: Synchronize podman-compose directory
synchronize:
src: gerrit-podman/
dest: /etc/gerrit-podman/
- name: Create Gerrit Group
group:
name: "{{ gerrit_user_name }}"
gid: "{{ gerrit_id }}"
system: yes
- name: Create Gerrit User
user:
name: "{{ gerrit_user_name }}"
uid: "{{ gerrit_id }}"
comment: Gerit User
shell: /bin/bash
home: "{{ gerrit_home_dir }}"
group: "{{ gerrit_user_name }}"
create_home: yes
system: yes
- name: Ensure review_site directory exists
file:
state: directory
path: "{{ gerrit_site_dir }}"
owner: "{{ gerrit_user_name }}"
group: "{{ gerrit_user_name }}"
mode: 0755
- name: Ensure Gerrit volume directories exists
file:
state: directory
path: "{{ gerrit_site_dir }}/{{ item }}"
owner: "{{ gerrit_user_name }}"
group: "{{ gerrit_user_name }}"
mode: 0755
loop:
- etc
- git
- index
- cache
- static
- hooks
- tmp
- logs