def _with_reconnect(self, fn): try: fn() except (pika.exceptions.ConnectionClosed, pika.exceptions.ChannelClosed): logger.debug("reconnecting rabbit") self.channel = rabbit.connect() fn()
def __init__(self): self.listeners = {} self.channel = rabbit.connect()
def consume(args): host = args.host port = args.port url = "http://{}:{}/listeners".format(host, port) config, _dir = parse_config(args) name = config.get('name') if not name: logger.error("No name set in config!") _exec = config['exec'] cwd = config.get('cwd') if cwd: _dir = cwd def connect(): disconnected = True logger.info("connecting {} on {}".format(name, url)) while disconnected: logger.debug("sleeping") time.sleep(1) try: resp = requests.post(url, json={"name": name, "config": config}) disconnected = resp.status_code != 200 except requests.ConnectionError: logger.debug("couldn't connect") def update_config(new_config): _config, _dir = parse_config(args) name = _config['name'] if _dir is None: return if not new_config: logger.info("skipping update of empty config") return if 'name' in new_config: del new_config['name'] _config.update(new_config) with open(os.path.join(_dir, args.config), 'w') as config_file: logger.info("updating config {}".format(config_file)) config_file.write(json.dumps(_config, indent=2)) requests.put(url, json={"name": name, "config": _config}) def notify(notify_data): url = "http://{}:{}/listeners".format(host, port) logger.debug("notifying {} on {}".format(name, url)) try: resp = requests.put(url, json=dict(notify_data, name=name)) if resp.status_code != 200: logger.error("error on notify: {}".format(resp.text)) except requests.ConnectionError: logger.error("couldn't notify") def discord_notify(_config, msg): hook = _config.get("discord_hook") if hook: hook_error = None try: discord_user = _config.get("discord_notify", '') discord_msg = "{} {}".format(discord_user, msg)\ .replace(name, '`{}`'.format(name))\ .replace('FAILED', '**FAILED**') resp = requests.post(hook, json={"content": discord_msg}) if resp.status_code != 200: hook_error = resp.text except requests.RequestException as re: hook_error = str(re) logger.exception("hook failed") if hook_error: logger.error("Hook failed to execute: {}".format(hook_error)) else: logger.debug("hook ok") def run_hook(_config, parsed, updated_branch): notify_data = {"msg": "running hook", "branch": updated_branch} for attr in ['pusher', 'latest_hash']: notify_data[attr] = parsed.get(attr) notify(notify_data) discord_notify(_config, "{pusher} {msg} {name} on {branch}".format(**dict(notify_data, name=name))) worked, output = run(_exec, _dir, env={'branch': updated_branch}, logger=logger) msg = "CI job {} {} on branch({})".format(name, "succeeded" if worked else "FAILED", updated_branch) notify_data['msg'] = msg if not worked: notify_data['output'] = output.split('\n') notify(notify_data) logger.info(msg) if output: logger.info(output) if not worked: logger.info("[no output]") discord_msg = msg if not worked: discord_msg += "\n```{}```".format(output[:1800]) if output else "`[no output]`" discord_notify(_config, discord_msg) def handle_push(_config, parsed, updated_branch): branch_filter = _config.get('branch_filter') branch = parsed.get("branch") filtered = False if branch_filter: if not isinstance(branch_filter, list): logger.error("branch filter isn't a list") filtered = True elif branch not in branch_filter: logger.info("skipping, branch '{}' not in {}".format(branch, branch_filter)) filtered = True files_filter = _config.get("files_filter") if not filtered and files_filter: if not isinstance(files_filter, list): filtered = True logger.error("files filter must be a list") else: all_modified = parsed.get("all_modified") filtered = True for modified in all_modified: for file_filter in files_filter: if modified.find(file_filter) != -1: logger.debug("found filter match {} with {}".format(file_filter, modified)) filtered = False break if filtered: logger.debug("modified {} not in {}".format(all_modified, files_filter)) logger.info("no match in file filters") if filtered: logger.debug("skipping b/c of filter") else: lock_file = _config.get('lock_file') if lock_file: logger.info("acquiring lock {}".format(lock_file)) timeout = 3 * 60 try: with portalocker.Lock(lock_file, timeout=timeout): run_hook(_config, parsed, updated_branch) except portalocker.exceptions.LockException: logger.error("lock acquisition timeout on {} after {}".format(lock_file, timeout)) else: run_hook(_config, parsed, updated_branch) def recv(ch, method, properties, body): logger.debug("calling receive on {}".format(body)) #reloading config _config, _ = parse_config(args) try: parsed = json.loads(body) action = parsed.get("action") updated_branch = parsed.get("branch", 'master') if action == "push": handle_push(_config, parsed, updated_branch) elif action == "shutdown": logger.info("received shutdown signal, reconnecting") connect() logger.info("reconnected") elif action == 'update': update_config(parsed.get('data', {})) elif action == "created": logger.info("successfully connected") else: logger.info("not recognized action: {}".format(action)) except: logger.exception("error when trying to load body: {}".format(body)) channel = rabbit.connect(queue=name, host=args.rabbit_host) channel.basic_consume(recv, queue=name, no_ack=True) logger.info("consuming") connect() channel.start_consuming()