def main(): try: current_release = int(open(release_file).read().strip()) except: current_release = 0 data = {'old_release': current_release, 'hostname': socket.gethostname()} log.debug('Sending: {}', data) response = server_request('/penguindome/v1/update', data=data, exit_on_connection_error=True) data = response.json() changed = False if data['status'] == 'current': log.debug('Current') elif data['status'] == 'out-of-date': do_release(data) changed = True else: log.exception('Unrecognized status: {}'.format(data['status'])) sys.exit(1) if 'patches' in data: do_patches(data['patches']) changed = True if changed: subprocess.check_output(os.path.join('bin', 'verify')) log.info('Successful update') sys.exit(42)
def do_patches(patches): for patch in patches: patch_id = patch['id'] for patch_file in patch['files']: patch_path = patch_file['path'] patch_mode = patch_file['mode'] patch_content = b64decode(patch_file.get('content', '')) if patch_path.startswith('/'): log.error('Ignoring absolute patch {}', patch_path) continue if '..' in os.sep.split(patch_path): log.error('Ignoring patch {} with ".." in it', patch_path) continue if patch_mode == 0: if patch_content: log.error( "Patch for {}, mode 0, has content but shouldn't", patch_path) continue if os.path.exists(patch_path): log.info('Removing {} due to patch', patch_path) try: os.remove(patch_path) except FileNotFoundError: log.warn('Failed to delete {} (already gone)', patch_path) pass else: log.warn("Patch says to remove {} but it's already gone", patch_path) continue log.info('Patching {} (id {}, mode {:o})', patch_path, patch_id, patch_mode) patch_dir = os.path.dirname(os.path.join(top_dir, patch_path)) os.makedirs(patch_dir, exist_ok=True) open(patch_path, 'wb').write(patch_content) os.chmod(patch_path, patch_mode) server_request('/penguindome/v1/acknowledge_patch', data={ 'id': patch_id, 'hostname': socket.gethostname() }, exit_on_connection_error=True)
def _request(self, request, data=None): if data is None: data = {} else: data = data.copy() if self.pipe_id: data['pipe_id'] = self.pipe_id response = server_request('/penguindome/v1/server_pipe/{}/{}'.format( self.type, request), data=data, local_port=self.local_port, logger=self.logger, signed=request not in ('send', 'receive')) if response.status_code == 404: raise FileNotFoundError('Pipe ID {} not found'.format( self.pipe_id)) response.raise_for_status() return response.json()
from requests.exceptions import HTTPError, ReadTimeout import sys from penguindome import top_dir, collected_dir, set_gpg from penguindome.client import get_logger, server_request log = get_logger('submit') os.chdir(top_dir) set_gpg('client') for collected in sorted(glob.glob(os.path.join(collected_dir, '*[0-9]'))): # This nested try/except is that we don't have to duplicate the code twice, # one for unrecognized HTTPError exceptiosns and again for ReadTimeout. try: try: server_request('/penguindome/v1/submit', data_path=collected, exit_on_connection_error=True, logger=log) except HTTPError as e: if e.response.status_code == 400: log.error('Server returned status code 400. ' 'Renaming {} to {}.bad.', collected, collected) os.rename(collected, collected + '.bad') sys.exit(1) raise except (HTTPError, ReadTimeout) as e: log.error('Submit failed: {}', str(e)) log.debug('Traceback of failed submission', exc_info=sys.exc_info()) sys.exit(1) os.unlink(collected) log.debug('Successful submission of {}', collected)