1
0
mirror of https://github.com/gryf/ferrit.git synced 2026-02-07 16:55:47 +01:00
Files
ferrit/gerrit_fake_ssh_server.py
Szymon Datko e547a9a507 Implement simple events pusher
Just POC, but works !!!
2019-11-01 08:10:37 +01:00

201 lines
11 KiB
Python

#!/usr/bin/env python
import inspect
import logging
import os
import socketserver
import sys
import threading
import time
import traceback
import paramiko
# it could be even 29418, which is standard gerrit port
PORT = 2200
FILE_DIR = os.path.dirname(__file__)
BASE_NAME = os.path.extsep.join(os.path.basename(__file__)
.split(os.path.extsep)[:-1])
HOST_KEY = paramiko.RSAKey(filename=os.path.join(FILE_DIR,
'gerrit-server-key'),
password='jenkins')
GERRIT_CMD_PROJECTS = """All-Projects
All-Users
openstack
openstack/nova
openstack/neutron
"""
GERRIT_CMD_VERSION = "gerrit version 2.16.7\n"
GERRIT_SHELL_MSG = """\r
**** Welcome to Ferrit Code Review ****\r
\r
Hi Jenkins, you have successfully connected over SSH.\r
\r
Unfortunately, interactive shells are disabled.\r
To clone a hosted Git repository, use:\r
\r
git clone ssh://localhost:{GERRIT_PORT}/REPOSITORY_NAME.git\r
\r
""".format(GERRIT_PORT=PORT)
GERRIT_STREAM_EVENTS = """{"submitter":{"name":"Some Author","email":"some.author@nonexistent.com","username":"some.author"},"refUpdate":{"oldRev":"0000000000000000000000000000000000000000","newRev":"9108f306de1364fa9f6f510b189b3de1c2a31365","refName":"refs/changes/33/3033/1","project":"openstack/nova"},"type":"ref-updated","eventCreatedOn":1572010525}
{"submitter":{"name":"Some Author","email":"some.author@nonexistent.com","username":"some.author"},"refUpdate":{"oldRev":"0000000000000000000000000000000000000000","newRev":"9ad7fd292cc092b52730d92aa9db7c90f98eab9e","refName":"refs/changes/33/3033/meta","project":"openstack/nova"},"type":"ref-updated","eventCreatedOn":1572010525}
{"uploader":{"name":"Some Author","email":"some.author@nonexistent.com","username":"some.author"},"patchSet":{"number":1,"revision":"9108f306de1364fa9f6f510b189b3de1c2a31365","parents":["eb37540e5ffd5326e18a4c72ea6660a04a368728"],"ref":"refs/changes/33/3033/1","uploader":{"name":"Some Author","email":"some.author@nonexistent.com","username":"some.author"},"createdOn":1572010523,"author":{"name":"Some Author","email":"some.author@nonexistent.com","username":"some.author"},"kind":"REWORK","sizeInsertions":1,"sizeDeletions":-1},"change":{"project":"openstack/nova","branch":"master","id":"I920acf0faf8f78f5c1b4a5bc86e3715e2df4b953","number":3033,"subject":"Grammar quickfix.","owner":{"name":"Some Author","email":"some.author@nonexistent.com","username":"some.author"},"url":"https://gerrit-server.net/c/openstack/nova/+/3033","commitMessage":"Grammar quickfix.\\n\\nChange-Id: I920acf0faf8f78f5c1b4a5bc86e3715e2df4b953\\n","createdOn":1572010523,"status":"NEW"},"project":"openstack/nova","refName":"refs/heads/master","changeKey":{"id":"I920acf0faf8f78f5c1b4a5bc86e3715e2df4b953"},"type":"patchset-created","eventCreatedOn":1572010525}
"""
a = """
{"submitter":{"name":"Some Author","email":"some.author@nonexistent.com","username":"some.author"},"refUpdate":{"oldRev":"9ad7fd292cc092b52730d92aa9db7c90f98eab9e","newRev":"c127a8817d8a273c2d1ad0a028fb8ed94dff679a","refName":"refs/changes/33/3033/meta","project":"openstack/nova"},"type":"ref-updated","eventCreatedOn":1572010528}
{"reviewer":{"name":"Other Guy","email":"other.guy@nonexistent.com","username":"other.guy"},"patchSet":{"number":1,"revision":"9108f306de1364fa9f6f510b189b3de1c2a31365","parents":["eb37540e5ffd5326e18a4c72ea6660a04a368728"],"ref":"refs/changes/33/3033/1","uploader":{"name":"Some Author","email":"some.author@nonexistent.com","username":"some.author"},"createdOn":1572010523,"author":{"name":"Some Author","email":"some.author@nonexistent.com","username":"some.author"},"kind":"REWORK","sizeInsertions":1,"sizeDeletions":-1},"change":{"project":"openstack/nova","branch":"master","id":"I920acf0faf8f78f5c1b4a5bc86e3715e2df4b953","number":3033,"subject":"Grammar quickfix.","owner":{"name":"Some Author","email":"some.author@nonexistent.com","username":"some.author"},"url":"https://gerrit-server.net/c/openstack/nova/+/3033","commitMessage":"Grammar quickfix.\n\nChange-Id: I920acf0faf8f78f5c1b4a5bc86e3715e2df4b953\n","createdOn":1572010523,"status":"NEW"},"project":"openstack/nova","refName":"refs/heads/master","changeKey":{"id":"I920acf0faf8f78f5c1b4a5bc86e3715e2df4b953"},"type":"reviewer-added","eventCreatedOn":1572010528}
{"submitter":{"name":"Jenkins","email":"jenkins@gerrit-server.net","username":"jenkins"},"refUpdate":{"oldRev":"c127a8817d8a273c2d1ad0a028fb8ed94dff679a","newRev":"c2f1105cf159b0a2812096c5803e07ebb7c7407f","refName":"refs/changes/33/3033/meta","project":"openstack/nova"},"type":"ref-updated","eventCreatedOn":1572010535}
{"author":{"name":"Jenkins","email":"jenkins@gerrit-server.net","username":"jenkins"},"approvals":[{"type":"Verified","description":"Verified","value":"0"},{"type":"Code-Review","description":"Code-Review","value":"0"},{"type":"Workflow","description":"Workflow","value":"0"}],"comment":"Patch Set 1:\n\nBuild Started https://jenkins.dev.cloud.company.net/job/Review/job/Unit-tests/5752//console","patchSet":{"number":1,"revision":"9108f306de1364fa9f6f510b189b3de1c2a31365","parents":["eb37540e5ffd5326e18a4c72ea6660a04a368728"],"ref":"refs/changes/33/3033/1","uploader":{"name":"Some Author","email":"some.author@nonexistent.com","username":"some.author"},"createdOn":1572010523,"author":{"name":"Some Author","email":"some.author@nonexistent.com","username":"some.author"},"kind":"REWORK","sizeInsertions":1,"sizeDeletions":-1},"change":{"project":"openstack/nova","branch":"master","id":"I920acf0faf8f78f5c1b4a5bc86e3715e2df4b953","number":3033,"subject":"Grammar quickfix.","owner":{"name":"Some Author","email":"some.author@nonexistent.com","username":"some.author"},"url":"https://gerrit-server.net/c/openstack/nova/+/3033","commitMessage":"Grammar quickfix.\n\nChange-Id: I920acf0faf8f78f5c1b4a5bc86e3715e2df4b953\n","createdOn":1572010523,"status":"NEW"},"project":"openstack/nova","refName":"refs/heads/master","changeKey":{"id":"I920acf0faf8f78f5c1b4a5bc86e3715e2df4b953"},"type":"comment-added","eventCreatedOn":1572010535}
"""
LOG = logging.getLogger(__name__)
LOG.setLevel(logging.DEBUG)
handler = logging.FileHandler(os.path.join(FILE_DIR, BASE_NAME + '.log'))
handler.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] '
'%(filename)s:%(lineno)s - '
'%(message)s'))
LOG.addHandler(handler)
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] '
'%(filename)s:%(lineno)s - '
'%(message)s'))
LOG.addHandler(handler)
class Server(paramiko.ServerInterface):
def __init__(self, client_address):
LOG.debug('%s', inspect.stack()[0][3])
LOG.debug('client_address: %s', client_address)
self.command = None
self.event = threading.Event()
self.client_address = client_address
def check_channel_request(self, kind, chanid):
LOG.debug('%s', inspect.stack()[0][3])
LOG.debug('kind: %s, chanid: %s', kind, chanid)
if kind == 'session':
return paramiko.OPEN_SUCCEEDED
return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
def get_allowed_auths(self, username):
LOG.debug('%s', inspect.stack()[0][3])
LOG.debug('username: %s', username)
return "password,publickey"
def check_auth_password(self, username, password):
LOG.debug('%s', inspect.stack()[0][3])
LOG.debug('username: %s, password: %s', username, password)
return paramiko.AUTH_SUCCESSFUL
def check_auth_publickey(self, username, key):
LOG.debug('%s', inspect.stack()[0][3])
LOG.debug('username: %s, key: %s', username, str(key)[:11])
return paramiko.AUTH_SUCCESSFUL
def check_channel_exec_request(self, channel, command):
LOG.debug('%s', inspect.stack()[0][3])
LOG.debug('channel: %s, command: %s', channel.get_id(), command)
self.command = command
self.event.set()
return True
def check_channel_shell_request(self, channel):
LOG.debug('%s', inspect.stack()[0][3])
LOG.debug('channel: %s', channel.get_id())
self.event.set()
return True
def check_global_request(self, kind, msg):
LOG.debug('%s', inspect.stack()[0][3])
LOG.debug('kind: %s, msg: %s', kind, msg.get_text())
return True
def check_channel_env_request(self, channel, name, value):
LOG.debug('%s', inspect.stack()[0][3])
LOG.debug("channel: %s, name: %s, value: %s",
channel.get_id(), name, value)
return True
def check_channel_pty_request(self, channel, term, width, height,
pixelwidth, pixelheight, modes):
LOG.debug('%s', inspect.stack()[0][3])
LOG.debug("channel: %s, term: %s, width: %s, height: %s, "
"pixelwidth: %s, pixelheight: %s, modes: %s",
channel.get_id(), term, width, height,
pixelwidth, pixelheight, str(modes)[:7])
return True
class SSHHandler(socketserver.StreamRequestHandler):
def handle(self):
try:
transport = paramiko.Transport(self.connection)
transport.add_server_key(HOST_KEY)
server = Server(self.client_address)
try:
transport.start_server(server=server)
except paramiko.SSHException:
return
while True:
# wait for auth
channel = transport.accept(20)
if channel is None:
transport.close()
return 1
server.event.wait(10)
if not server.event.is_set():
transport.close()
return 1
if server.command:
cmd = server.command.decode('utf-8')
LOG.debug('received server command: %s', cmd)
if cmd == 'gerrit version':
LOG.debug('sending version string')
channel.send(GERRIT_CMD_VERSION)
channel.close()
elif cmd == 'gerrit ls-projects':
LOG.debug('sending list of projects')
channel.send(GERRIT_CMD_PROJECTS)
channel.close()
elif cmd == 'gerrit stream-events':
LOG.debug('sending events from queue...')
while True:
time.sleep(10)
channel.send(GERRIT_STREAM_EVENTS)
pass # TODO: implement queue or something here
else:
LOG.debug('unknown command -- closing channel')
channel.close()
else:
LOG.debug('requested interactive session')
channel.send_stderr(GERRIT_SHELL_MSG)
channel.makefile("rU").read(1) # wait for any key press
return
except Exception:
traceback.print_exc()
finally:
transport.close()
def main():
sshserver = socketserver.ThreadingTCPServer(('127.0.0.1', PORT),
SSHHandler)
sshserver.serve_forever()
if __name__ == "__main__":
main()