def test_change_error(self): change = pebble.Change( id=pebble.ChangeID('1234'), kind='start', summary='Start service "foo"', status='Done', tasks=[], ready=True, err=None, spawn_time=datetime.datetime.now(), ready_time=datetime.datetime.now(), ) error = pebble.ChangeError('Some error', change) self.assertIsInstance(error, pebble.Error) self.assertEqual(error.err, 'Some error') self.assertEqual(error.change, change) self.assertEqual(str(error), 'Some error')
def test_change_init(self): change = pebble.Change( id=pebble.ChangeID('70'), kind='autostart', err='SILLY', ready=True, ready_time=datetime_nzdt(2021, 1, 28, 14, 37, 4, 291517), spawn_time=datetime_nzdt(2021, 1, 28, 14, 37, 2, 247202), status='Done', summary='Autostart service "svc"', tasks=[], ) self.assertEqual(change.id, '70') self.assertEqual(change.kind, 'autostart') self.assertEqual(change.err, 'SILLY') self.assertEqual(change.ready, True) self.assertEqual(change.ready_time, datetime_nzdt(2021, 1, 28, 14, 37, 4, 291517)) self.assertEqual(change.spawn_time, datetime_nzdt(2021, 1, 28, 14, 37, 2, 247202)) self.assertEqual(change.status, 'Done') self.assertEqual(change.summary, 'Autostart service "svc"') self.assertEqual(change.tasks, [])
def test_change_id(self): change_id = pebble.ChangeID('1234') self.assertEqual(change_id, '1234')
def main(): parser = argparse.ArgumentParser() parser.add_argument( '--socket', help='pebble socket path, default $PEBBLE/.pebble.socket') subparsers = parser.add_subparsers(dest='command', metavar='command') p = subparsers.add_parser('abort', help='abort a change by ID') p.add_argument('change_id', help='ID of change to abort') p = subparsers.add_parser('ack', help='acknowledge warnings up to given time') p.add_argument( '--timestamp', help='time to acknowledge up to (YYYY-mm-ddTHH:MM:SS.f+ZZ:zz' 'format), default current time', type=pebble._parse_timestamp) p = subparsers.add_parser('autostart', help='autostart default service(s)') p = subparsers.add_parser('change', help='show a single change by ID') p.add_argument('change_id', help='ID of change to fetch') p = subparsers.add_parser('changes', help='show (filtered) changes') p.add_argument('--select', help='change state to filter on, default %(default)s', choices=[s.value for s in pebble.ChangeState], default='all') p.add_argument('--service', help='optional service name to filter on') p = subparsers.add_parser('start', help='start service(s)') p.add_argument('service', help='name of service to start (can specify multiple)', nargs='+') p = subparsers.add_parser('stop', help='stop service(s)') p.add_argument('service', help='name of service to stop (can specify multiple)', nargs='+') p = subparsers.add_parser('system-info', help='show Pebble system information') p = subparsers.add_parser('warnings', help='show (filtered) warnings') p.add_argument('--select', help='warning state to filter on, default %(default)s', choices=[s.value for s in pebble.WarningState], default='all') args = parser.parse_args() if not args.command: parser.error('argument command: required') socket_path = args.socket if socket_path is None: pebble_env = os.getenv('PEBBLE') if not pebble_env: print( 'cannot create Pebble client (set PEBBLE or specify --socket)', file=sys.stderr) sys.exit(1) socket_path = os.path.join(pebble_env, '.pebble.socket') client = pebble.Client(socket_path=socket_path) try: if args.command == 'abort': result = client.abort_change(pebble.ChangeID(args.change_id)) elif args.command == 'ack': timestamp = args.timestamp or datetime.datetime.now( tz=datetime.timezone.utc) result = client.ack_warnings(timestamp) elif args.command == 'autostart': result = client.autostart_services() elif args.command == 'change': result = client.get_change(pebble.ChangeID(args.change_id)) elif args.command == 'changes': result = client.get_changes(select=pebble.ChangeState(args.select), service=args.service) elif args.command == 'start': result = client.start_services(args.service) elif args.command == 'stop': result = client.stop_services(args.service) elif args.command == 'system-info': result = client.get_system_info() elif args.command == 'warnings': result = client.get_warnings( select=pebble.WarningState(args.select)) else: raise AssertionError("shouldn't happen") except pebble.APIError as e: print('{} {}: {}'.format(e.code, e.status, e.message), file=sys.stderr) sys.exit(1) except pebble.ConnectionError as e: print('cannot connect to socket {!r}: {}'.format(socket_path, e), file=sys.stderr) sys.exit(1) except pebble.ChangeError as e: print('ChangeError:', e, file=sys.stderr) sys.exit(1) if isinstance(result, list): for x in result: print(x) else: print(result)
def main(): parser = argparse.ArgumentParser() parser.add_argument( '--socket', help='pebble socket path, default $PEBBLE/.pebble.socket') subparsers = parser.add_subparsers(dest='command', metavar='command') p = subparsers.add_parser('abort', help='abort a change by ID') p.add_argument('change_id', help='ID of change to abort') p = subparsers.add_parser('ack', help='acknowledge warnings up to given time') p.add_argument( '--timestamp', help='time to acknowledge up to (YYYY-mm-ddTHH:MM:SS.f+ZZ:zz' 'format), default current time', type=pebble._parse_timestamp) p = subparsers.add_parser('add', help='add a configuration layer dynamically') p.add_argument('--combine', action='store_true', help='combine layer instead of appending') p.add_argument('label', help='label for new layer') p.add_argument('layer_path', help='path of layer YAML file') p = subparsers.add_parser('autostart', help='autostart default service(s)') p = subparsers.add_parser('change', help='show a single change by ID') p.add_argument('change_id', help='ID of change to fetch') p = subparsers.add_parser('changes', help='show (filtered) changes') p.add_argument('--select', help='change state to filter on, default %(default)s', choices=[s.value for s in pebble.ChangeState], default='all') p.add_argument('--service', help='optional service name to filter on') p = subparsers.add_parser('ls', help='list files') p.add_argument('-d', '--directory', action='store_true', help='list directories themselves, not their contents') p.add_argument('-p', '--pattern', help='glob pattern to filter results') p.add_argument('path', help='name of directory or file') p = subparsers.add_parser('mkdir', help='create directory') p.add_argument('-p', '--parents', action='store_true', help='create parent directories if needed') p.add_argument('path', help='path to create') p = subparsers.add_parser('plan', help='show configuration plan (combined layers)') p = subparsers.add_parser('pull', help='copy file from remote system') p.add_argument('remote_path', help='path of remote file') p.add_argument('local_path', help='path of local file to copy to') p = subparsers.add_parser('push', help='copy file to remote system') p.add_argument('-d', '--dirs', action='store_true', help='create parent directories') p.add_argument('-m', '--mode', help='3-digit octal permissions') p.add_argument('-u', '--user', help='user to set') p.add_argument('-g', '--group', help='group to set') p.add_argument('local_path', help='path of local file') p.add_argument('remote_path', help='path of remote file to copy to') p = subparsers.add_parser('rm', help='remove path') p.add_argument('-r', '--recursive', action='store_true', help='recursively delete directory contents') p.add_argument('path', help='path to remove') p = subparsers.add_parser('services', help='show service status') p.add_argument('service', help='name of service (none means all; multiple ok)', nargs='*') p = subparsers.add_parser('start', help='start service(s)') p.add_argument('service', help='name of service to start (can specify multiple)', nargs='+') p = subparsers.add_parser('stop', help='stop service(s)') p.add_argument('service', help='name of service to stop (can specify multiple)', nargs='+') p = subparsers.add_parser('system-info', help='show Pebble system information') p = subparsers.add_parser('warnings', help='show (filtered) warnings') p.add_argument('--select', help='warning state to filter on, default %(default)s', choices=[s.value for s in pebble.WarningState], default='all') args = parser.parse_args() if not args.command: parser.error('argument command: required') socket_path = args.socket if socket_path is None: pebble_env = os.getenv('PEBBLE') if not pebble_env: print( 'cannot create Pebble client (set PEBBLE or specify --socket)', file=sys.stderr) sys.exit(1) socket_path = os.path.join(pebble_env, '.pebble.socket') client = pebble.Client(socket_path=socket_path) try: if args.command == 'abort': result = client.abort_change(pebble.ChangeID(args.change_id)) elif args.command == 'ack': timestamp = args.timestamp or datetime.datetime.now( tz=datetime.timezone.utc) result = client.ack_warnings(timestamp) elif args.command == 'add': try: with open(args.layer_path, encoding='utf-8') as f: layer_yaml = f.read() except IOError as e: parser.error('cannot read layer YAML: {}'.format(e)) client.add_layer(args.label, layer_yaml, combine=bool(args.combine)) result = 'Layer {!r} added successfully from {!r}'.format( args.label, args.layer_path) elif args.command == 'autostart': result = client.autostart_services() elif args.command == 'change': result = client.get_change(pebble.ChangeID(args.change_id)) elif args.command == 'changes': result = client.get_changes(select=pebble.ChangeState(args.select), service=args.service) elif args.command == 'ls': result = client.list_files(args.path, pattern=args.pattern, itself=args.directory) elif args.command == 'mkdir': client.make_dir(args.path, make_parents=bool(args.parents)) result = 'created remote directory {}'.format(args.path) elif args.command == 'plan': result = client.get_plan().to_yaml() elif args.command == 'pull': content = client.pull(args.remote_path, encoding=None).read() if args.local_path != '-': with open(args.local_path, 'wb') as f: f.write(content) result = 'wrote remote file {} to {}'.format( args.remote_path, args.local_path) else: sys.stdout.buffer.write(content) return elif args.command == 'push': with open(args.local_path, 'rb') as f: client.push(args.remote_path, f, make_dirs=args.dirs, permissions=int(args.mode, 8) if args.mode is not None else None, user=args.user, group=args.group) result = 'wrote {} to remote file {}'.format( args.local_path, args.remote_path) elif args.command == 'rm': client.remove_path(args.path, recursive=bool(args.recursive)) result = 'removed remote path {}'.format(args.path) elif args.command == 'services': result = client.get_services(args.service) elif args.command == 'start': result = client.start_services(args.service) elif args.command == 'stop': result = client.stop_services(args.service) elif args.command == 'system-info': result = client.get_system_info() elif args.command == 'warnings': result = client.get_warnings( select=pebble.WarningState(args.select)) else: raise AssertionError("shouldn't happen") except pebble.APIError as e: print('{} {}: {}'.format(e.code, e.status, e.message), file=sys.stderr) sys.exit(1) except pebble.ConnectionError as e: print('cannot connect to socket {!r}: {}'.format(socket_path, e), file=sys.stderr) sys.exit(1) except pebble.ChangeError as e: print('ChangeError:', e, file=sys.stderr) sys.exit(1) if isinstance(result, list): for x in result: print(x) else: print(result)
def main(): parser = argparse.ArgumentParser() parser.add_argument('--socket', help='pebble socket path, default $PEBBLE/.pebble.socket') subparsers = parser.add_subparsers(dest='command', metavar='command') p = subparsers.add_parser('abort', help='abort a change by ID') p.add_argument('change_id', help='ID of change to abort') p = subparsers.add_parser('ack', help='acknowledge warnings up to given time') p.add_argument('--timestamp', help='time to acknowledge up to (YYYY-mm-ddTHH:MM:SS.f+ZZ:zz' 'format), default current time', type=pebble._parse_timestamp) p = subparsers.add_parser('add', help='add a configuration layer dynamically') p.add_argument('--combine', action='store_true', help='combine layer instead of appending') p.add_argument('label', help='label for new layer') p.add_argument('layer_path', help='path of layer YAML file') p = subparsers.add_parser('autostart', help='autostart default service(s)') p = subparsers.add_parser('change', help='show a single change by ID') p.add_argument('change_id', help='ID of change to fetch') p = subparsers.add_parser('changes', help='show (filtered) changes') p.add_argument('--select', help='change state to filter on, default %(default)s', choices=[s.value for s in pebble.ChangeState], default='all') p.add_argument('--service', help='optional service name to filter on') p = subparsers.add_parser('checks', help='show (filtered) checks') p.add_argument('--level', help='check level to filter on, default all levels', choices=[c.value for c in pebble.CheckLevel], default='') p.add_argument('name', help='check name(s) to filter on', nargs='*') p = subparsers.add_parser('exec', help='execute a command') p.add_argument('--env', help='environment variables to set', action='append', metavar='KEY=VALUE') p.add_argument('--working-dir', help='working directory to run command in') p.add_argument('--io-mode', help='input/output mode, default %(default)r', choices=['passthrough', 'string'], default='passthrough') p.add_argument('-t', '--timeout', type=float, help='timeout in seconds') p.add_argument('-u', '--user', help='user to run as') p.add_argument('-g', '--group', help='group to run as') p.add_argument('--encoding', help="input/output encoding or 'none', default %(default)r", default='utf-8') p.add_argument('--combine-stderr', help='combine stderr into stdout', action='store_true') p.add_argument('exec_command', help='command and arguments', nargs='+', metavar='command') p = subparsers.add_parser('ls', help='list files') p.add_argument('-d', '--directory', action='store_true', help='list directories themselves, not their contents') p.add_argument('-p', '--pattern', help='glob pattern to filter results') p.add_argument('path', help='name of directory or file') p = subparsers.add_parser('mkdir', help='create directory') p.add_argument('-p', '--parents', action='store_true', help='create parent directories if needed') p.add_argument('path', help='path to create') p = subparsers.add_parser('plan', help='show configuration plan (combined layers)') p = subparsers.add_parser('pull', help='copy file from remote system') p.add_argument('remote_path', help='path of remote file') p.add_argument('local_path', help='path of local file to copy to') p = subparsers.add_parser('push', help='copy file to remote system') p.add_argument('-d', '--dirs', action='store_true', help='create parent directories') p.add_argument('-m', '--mode', help='3-digit octal permissions') p.add_argument('-u', '--user', help='user to set') p.add_argument('-g', '--group', help='group to set') p.add_argument('local_path', help='path of local file') p.add_argument('remote_path', help='path of remote file to copy to') p = subparsers.add_parser('rm', help='remove path') p.add_argument('-r', '--recursive', action='store_true', help='recursively delete directory contents') p.add_argument('path', help='path to remove') p = subparsers.add_parser('services', help='show service status') p.add_argument('service', help='name of service (none means all; multiple ok)', nargs='*') p = subparsers.add_parser('start', help='start service(s)') p.add_argument('service', help='name of service to start (can specify multiple)', nargs='+') p = subparsers.add_parser('stop', help='stop service(s)') p.add_argument('service', help='name of service to stop (can specify multiple)', nargs='+') p = subparsers.add_parser('system-info', help='show Pebble system information') p = subparsers.add_parser('wait', help='wait for a change by ID') p.add_argument('-t', '--timeout', type=float, help='timeout in seconds') p.add_argument('change_id', help='ID of change to wait for') p = subparsers.add_parser('warnings', help='show (filtered) warnings') p.add_argument('--select', help='warning state to filter on, default %(default)s', choices=[s.value for s in pebble.WarningState], default='all') args = parser.parse_args() if not args.command: parser.error('argument command: required') socket_path = args.socket if socket_path is None: pebble_env = os.getenv('PEBBLE') if not pebble_env: print('cannot create Pebble client (set PEBBLE or specify --socket)', file=sys.stderr) sys.exit(1) socket_path = os.path.join(pebble_env, '.pebble.socket') client = pebble.Client(socket_path=socket_path) try: if args.command == 'abort': result = client.abort_change(pebble.ChangeID(args.change_id)) elif args.command == 'ack': timestamp = args.timestamp or datetime.datetime.now(tz=datetime.timezone.utc) result = client.ack_warnings(timestamp) elif args.command == 'add': try: with open(args.layer_path, encoding='utf-8') as f: layer_yaml = f.read() except IOError as e: parser.error('cannot read layer YAML: {}'.format(e)) client.add_layer(args.label, layer_yaml, combine=bool(args.combine)) result = 'Layer {!r} added successfully from {!r}'.format( args.label, args.layer_path) elif args.command == 'autostart': result = client.autostart_services() elif args.command == 'change': result = client.get_change(pebble.ChangeID(args.change_id)) elif args.command == 'changes': result = client.get_changes(select=pebble.ChangeState(args.select), service=args.service) elif args.command == 'checks': result = client.get_checks(level=pebble.CheckLevel(args.level), names=args.name) elif args.command == 'exec': environment = {} for env in args.env or []: key, _, value = env.partition('=') environment[key] = value encoding = args.encoding if args.encoding != 'none' else None if args.io_mode == 'passthrough': if encoding is not None: stdin = sys.stdin stdout = sys.stdout stderr = sys.stderr if not args.combine_stderr else None else: stdin = sys.stdin.buffer stdout = sys.stdout.buffer stderr = sys.stderr.buffer if not args.combine_stderr else None else: if sys.stdin.isatty(): if encoding is not None: stdin = sys.stdin else: stdin = sys.stdin.buffer else: if encoding is not None: stdin = sys.stdin.read() else: stdin = sys.stdin.buffer.read() stdout = None stderr = None process = client.exec( args.exec_command, environment=environment, working_dir=args.working_dir, timeout=args.timeout, user=args.user, group=args.group, stdin=stdin, stdout=stdout, stderr=stderr, encoding=encoding, combine_stderr=args.combine_stderr, ) try: if args.io_mode == 'passthrough': process.wait() else: stdout, stderr = process.wait_output() print(repr(stdout)) if stderr: print(repr(stderr), end='', file=sys.stderr) sys.exit(0) except pebble.ExecError as e: print('ExecError:', e, file=sys.stderr) sys.exit(e.exit_code) elif args.command == 'ls': result = client.list_files(args.path, pattern=args.pattern, itself=args.directory) elif args.command == 'mkdir': client.make_dir(args.path, make_parents=bool(args.parents)) result = 'created remote directory {}'.format(args.path) elif args.command == 'plan': result = client.get_plan().to_yaml() elif args.command == 'pull': content = client.pull(args.remote_path, encoding=None).read() if args.local_path != '-': with open(args.local_path, 'wb') as f: f.write(content) result = 'wrote remote file {} to {}'.format(args.remote_path, args.local_path) else: sys.stdout.buffer.write(content) return elif args.command == 'push': with open(args.local_path, 'rb') as f: client.push( args.remote_path, f, make_dirs=args.dirs, permissions=int(args.mode, 8) if args.mode is not None else None, user=args.user, group=args.group) result = 'wrote {} to remote file {}'.format(args.local_path, args.remote_path) elif args.command == 'rm': client.remove_path(args.path, recursive=bool(args.recursive)) result = 'removed remote path {}'.format(args.path) elif args.command == 'services': result = client.get_services(args.service) elif args.command == 'start': result = client.start_services(args.service) elif args.command == 'stop': result = client.stop_services(args.service) elif args.command == 'system-info': result = client.get_system_info() elif args.command == 'wait': result = client.wait_change(args.change_id, timeout=args.timeout) elif args.command == 'warnings': result = client.get_warnings(select=pebble.WarningState(args.select)) else: raise AssertionError("shouldn't happen") except pebble.APIError as e: print('APIError: {} {}: {}'.format(e.code, e.status, e.message), file=sys.stderr) sys.exit(1) except pebble.ConnectionError as e: print('ConnectionError: cannot connect to socket {!r}: {}'.format(socket_path, e), file=sys.stderr) sys.exit(1) except pebble.ChangeError as e: print('ChangeError:', e, file=sys.stderr) sys.exit(1) if isinstance(result, list): for x in result: print(x) elif result is not None: print(result)
def main(): parser = argparse.ArgumentParser() parser.add_argument( '--socket', help='pebble socket path, default $PEBBLE/.pebble.socket') subparsers = parser.add_subparsers(dest='command', metavar='command') p = subparsers.add_parser('abort', help='abort a change by ID') p.add_argument('change_id', help='ID of change to abort') p = subparsers.add_parser('ack', help='acknowledge warnings up to given time') p.add_argument( '--timestamp', help='time to acknowledge up to (YYYY-mm-ddTHH:MM:SS.f+ZZ:zz' 'format), default current time', type=pebble._parse_timestamp) p = subparsers.add_parser('add', help='add a configuration layer dynamically') p.add_argument('--combine', action='store_true', help='combine layer instead of appending') p.add_argument('label', help='label for new layer') p.add_argument('layer_path', help='path of layer YAML file') p = subparsers.add_parser('autostart', help='autostart default service(s)') p = subparsers.add_parser('change', help='show a single change by ID') p.add_argument('change_id', help='ID of change to fetch') p = subparsers.add_parser('changes', help='show (filtered) changes') p.add_argument('--select', help='change state to filter on, default %(default)s', choices=[s.value for s in pebble.ChangeState], default='all') p.add_argument('--service', help='optional service name to filter on') p = subparsers.add_parser('plan', help='show configuration plan (combined layers)') p = subparsers.add_parser('services', help='show service status') p.add_argument('service', help='name of service (none means all; multiple ok)', nargs='*') p = subparsers.add_parser('start', help='start service(s)') p.add_argument('service', help='name of service to start (can specify multiple)', nargs='+') p = subparsers.add_parser('stop', help='stop service(s)') p.add_argument('service', help='name of service to stop (can specify multiple)', nargs='+') p = subparsers.add_parser('system-info', help='show Pebble system information') p = subparsers.add_parser('warnings', help='show (filtered) warnings') p.add_argument('--select', help='warning state to filter on, default %(default)s', choices=[s.value for s in pebble.WarningState], default='all') args = parser.parse_args() if not args.command: parser.error('argument command: required') socket_path = args.socket if socket_path is None: pebble_env = os.getenv('PEBBLE') if not pebble_env: print( 'cannot create Pebble client (set PEBBLE or specify --socket)', file=sys.stderr) sys.exit(1) socket_path = os.path.join(pebble_env, '.pebble.socket') client = pebble.Client(socket_path=socket_path) try: if args.command == 'abort': result = client.abort_change(pebble.ChangeID(args.change_id)) elif args.command == 'ack': timestamp = args.timestamp or datetime.datetime.now( tz=datetime.timezone.utc) result = client.ack_warnings(timestamp) elif args.command == 'add': try: with open(args.layer_path, encoding='utf-8') as f: layer_yaml = f.read() except IOError as e: parser.error('cannot read layer YAML: {}'.format(e)) client.add_layer(args.label, layer_yaml, combine=bool(args.combine)) result = 'Layer {!r} added successfully from {!r}'.format( args.label, args.layer_path) elif args.command == 'autostart': result = client.autostart_services() elif args.command == 'change': result = client.get_change(pebble.ChangeID(args.change_id)) elif args.command == 'changes': result = client.get_changes(select=pebble.ChangeState(args.select), service=args.service) elif args.command == 'plan': result = client.get_plan().to_yaml() elif args.command == 'services': result = client.get_services(args.service) elif args.command == 'start': result = client.start_services(args.service) elif args.command == 'stop': result = client.stop_services(args.service) elif args.command == 'system-info': result = client.get_system_info() elif args.command == 'warnings': result = client.get_warnings( select=pebble.WarningState(args.select)) else: raise AssertionError("shouldn't happen") except pebble.APIError as e: print('{} {}: {}'.format(e.code, e.status, e.message), file=sys.stderr) sys.exit(1) except pebble.ConnectionError as e: print('cannot connect to socket {!r}: {}'.format(socket_path, e), file=sys.stderr) sys.exit(1) except pebble.ChangeError as e: print('ChangeError:', e, file=sys.stderr) sys.exit(1) if isinstance(result, list): for x in result: print(x) else: print(result)