def wipe_main(args_parser): """ wipe mainline The mainline for the 'wipe' action. Args: args_parser: the argument parser to use for argument processing Returns: the exit code """ args_parser.add_argument('--danger', action='store_true') known_args = sys.argv[1:] args, unknown_args = args_parser.parse_known_args(known_args) if unknown_args: logger.warn('unknown arguments: {}'.format(' '.join(unknown_args))) work_dir = args.work_dir if args.work_dir else os.getcwd() # protection warning if not args.danger: print('') sys.stdout.flush() logger.warn('!!! DANGER DANGER DANGER !!!') print(""" A request has been made to attempt to wipe the pages from a configured Confluence instance. This is a helper utility call to assist a user in cleaning out a space since removing a bulk set of data may not be trivial for a user. Note that this action is not reversible with this tool and may require assistance from an administrator from a Confluence instance to recover pages. Only use this action if you know what you are doing. To use this action, the argument '--danger' must be set. """) sys.stdout.flush() logger.warn('!!! DANGER DANGER DANGER !!!') print('') return 1 # check configuration and prepare publisher publisher = None with TemporaryDirectory() as tmp_dir: with docutils_namespace(): app = Sphinx( work_dir, # document sources work_dir, # directory with configuration tmp_dir, # output for generated documents tmp_dir, # output for doctree files 'confluence') # builder to execute aggressive_search = app.config.confluence_adv_aggressive_search server_url = app.config.confluence_server_url space_name = app.config.confluence_space_name # initialize the publisher (if permitted) if app.config.confluence_publish: process_ask_configs(app.config) publisher = ConfluencePublisher() publisher.init(app.config) if not publisher: print('(error) publishing not configured in sphinx configuration') return 1 # reminder warning print('') sys.stdout.flush() logger.warn('!!! DANGER DANGER DANGER !!!') print(""" A request has been made to attempt to wipe the pages from a configured Confluence instance. This action is not reversible with this tool and may require assistance from an administrator from a Confluence instance to recover pages. Only use this action if you know what you are doing. """) sys.stdout.flush() logger.warn('!!! DANGER DANGER DANGER !!!') print('') if not ask_question('Are you sure you want to continue?'): return 0 print('') # user has confirmed; start an attempt to wipe publisher.connect() if aggressive_search: legacy_pages = publisher.getDescendantsCompat(None) else: legacy_pages = publisher.getDescendants(None) if not legacy_pages: print('No pages are published on this space. Exiting...') return 0 print(' URL:', server_url) print(' Space:', space_name) logger.note(' Pages: All Pages') print(' Total pages:', len(legacy_pages)) print('') if not ask_question('Are you sure you want to REMOVE these pages?'): return 0 print('') logger.info('Removing pages...', nonl=True) for page_id in legacy_pages: publisher.removePage(page_id) logger.info('.', nonl=True) logger.info(' done\n') return 0
def report_main(args_parser): """ report mainline The mainline for the 'report' action. Args: args_parser: the argument parser to use for argument processing Returns: the exit code """ args_parser.add_argument('--full-config', '-C', action='store_true') args_parser.add_argument('--no-sanitize', action='store_true') args_parser.add_argument('--offline', action='store_true') known_args = sys.argv[1:] args, unknown_args = args_parser.parse_known_args(known_args) if unknown_args: logger.warn('unknown arguments: {}'.format(' '.join(unknown_args))) rv = 0 offline = args.offline work_dir = args.work_dir if args.work_dir else os.getcwd() # setup sphinx engine to extract configuration config = {} configuration_load_issue = None confluence_instance_info = None publisher = ConfluencePublisher() try: with temp_dir() as tmp_dir: with docutils_namespace(): print('fetching configuration information...') builder = ConfluenceReportBuilder.name app = Sphinx( work_dir, # document sources work_dir, # directory with configuration tmp_dir, # output for built documents tmp_dir, # output for doctree files builder, # builder to execute status=sys.stdout, # sphinx status output warning=sys.stderr) # sphinx warning output if app.config.confluence_publish: try: process_ask_configs(app.config) except ConfluenceConfigurationError: offline = True # extract configuration information for k, v in app.config.values.items(): raw = getattr(app.config, k) if raw is None: continue if callable(raw): value = '(callable)' else: value = raw prefixes = ( 'confluence_', 'singleconfluence_', ) if not args.full_config and not k.startswith(prefixes): continue # always extract some known builder configurations if args.full_config and k.startswith(IGNORE_BUILDER_CONFS): continue config[k] = value # initialize the publisher (if needed later) publisher.init(app.config) except Exception: sys.stdout.flush() tb_msg = traceback.format_exc() logger.error(tb_msg) if os.path.isfile(os.path.join(work_dir, 'conf.py')): configuration_load_issue = 'unable to load configuration' configuration_load_issue += '\n\n' + tb_msg.strip() else: configuration_load_issue = 'no documentation/missing configuration' rv = 1 # attempt to fetch confluence instance version confluence_publish = config.get('confluence_publish') confluence_server_url = config.get('confluence_server_url') if not offline and confluence_publish and confluence_server_url: base_url = ConfluenceUtil.normalize_base_url(confluence_server_url) info = '' session = None try: print('connecting to confluence instance...') sys.stdout.flush() publisher.connect() info += ' connected: yes\n' session = publisher.rest_client.session except Exception: sys.stdout.flush() logger.error(traceback.format_exc()) info += ' connected: no\n' rv = 1 if session: try: # fetch print('fetching confluence instance information...') manifest_url = base_url + MANIFEST_PATH rsp = session.get(manifest_url) if rsp.status_code == 200: info += ' fetched: yes\n' # extract print('decoding information...') rsp.encoding = 'utf-8' raw_data = rsp.text info += ' decoded: yes\n' # parse print('parsing information...') xml_data = ElementTree.fromstring(raw_data) info += ' parsed: yes\n' root = ElementTree.ElementTree(xml_data) for o in root.findall('typeId'): info += ' type: ' + o.text + '\n' for o in root.findall('version'): info += ' version: ' + o.text + '\n' for o in root.findall('buildNumber'): info += ' build: ' + o.text + '\n' else: logger.error('bad response from server ({})'.format( rsp.status_code)) info += ' fetched: error ({})\n'.format(rsp.status_code) rv = 1 except Exception: sys.stdout.flush() logger.error(traceback.format_exc()) info += 'failure to determine confluence data\n' rv = 1 confluence_instance_info = info def sensitive_config(key): if key in config: if config[key]: config[key] = '(set)' else: config[key] = '(set; empty)' # always sanitize out sensitive information sensitive_config('confluence_client_cert_pass') sensitive_config('confluence_publish_headers') sensitive_config('confluence_publish_token') sensitive_config('confluence_server_pass') # optional sanitization if not args.no_sanitize: sensitive_config('author') sensitive_config('confluence_client_cert') sensitive_config('confluence_global_labels') sensitive_config('confluence_jira_servers') sensitive_config('confluence_mentions') sensitive_config('confluence_parent_page') sensitive_config('confluence_parent_page_id_check') sensitive_config('confluence_proxy') sensitive_config('confluence_publish_root') sensitive_config('confluence_server_auth') sensitive_config('confluence_server_cookies') sensitive_config('confluence_server_user') sensitive_config('project') # remove confluence instance (attempt to keep scheme) if 'confluence_server_url' in config: value = config['confluence_server_url'] parsed = urlparse(value) if parsed.scheme: value = parsed.scheme + '://<removed>' else: value = '(set; no scheme)' if parsed.netloc and parsed.netloc.endswith('atlassian.net'): value += ' (cloud)' config['confluence_server_url'] = value # remove space key, but track casing space_cfgs = [ 'confluence_space_key', 'confluence_space_name', # deprecated ] for space_cfg in space_cfgs: if space_cfg not in config: continue value = config[space_cfg] if value.startswith('~'): value = '(set; user)' elif value.isupper(): value = '(set; upper)' elif value.islower(): value = '(set; lower)' else: value = '(set; mixed)' config[space_cfg] = value print('') print('Confluence builder report has been generated.') print('Please copy the following text for the GitHub issue:') print('') logger.note('------------[ cut here ]------------') print('```') print('(system)') print(' platform:', single_line_version(platform.platform())) print(' python:', single_line_version(sys.version)) print(' sphinx:', single_line_version(sphinx_version)) print(' requests:', single_line_version(requests_version)) print(' builder:', single_line_version(scb_version)) print('') print('(configuration)') if config: for k, v in OrderedDict(sorted(config.items())).items(): print('{}: {}'.format(k, v)) else: print('~default configuration~') if configuration_load_issue: print('') print('(error loading configuration)') print(configuration_load_issue) if confluence_instance_info: print('') print('(confluence instance)') print(confluence_instance_info.rstrip()) print('```') logger.note('------------[ cut here ]------------') return rv
def wipe_main(args_parser): """ wipe mainline The mainline for the 'wipe' action. Args: args_parser: the argument parser to use for argument processing Returns: the exit code """ args_parser.add_argument('--danger', action='store_true') args_parser.add_argument('--parent', '-P', action='store_true') known_args = sys.argv[1:] args, unknown_args = args_parser.parse_known_args(known_args) if unknown_args: logger.warn('unknown arguments: {}'.format(' '.join(unknown_args))) work_dir = args.work_dir if args.work_dir else os.getcwd() # protection warning if not args.danger: print('') sys.stdout.flush() logger.warn('!!! DANGER DANGER DANGER !!!') print(""" A request has been made to attempt to wipe the pages from a configured Confluence instance. This is a helper utility call to assist a user in cleaning out a space since removing a bulk set of data may not be trivial for a user. Note that this action is not reversible with this tool and may require assistance from an administrator from a Confluence instance to recover pages. Only use this action if you know what you are doing. To use this action, the argument '--danger' must be set. """) sys.stdout.flush() logger.warn('!!! DANGER DANGER DANGER !!!') return 1 # check configuration and prepare publisher dryrun = False publisher = None try: with temp_dir() as tmp_dir: with docutils_namespace(): app = Sphinx( work_dir, # document sources work_dir, # directory with configuration tmp_dir, # output for built documents tmp_dir, # output for doctree files 'confluence', # builder to execute status=sys.stdout, # sphinx status output warning=sys.stderr) # sphinx warning output aggressive_search = app.config.confluence_adv_aggressive_search dryrun = app.config.confluence_publish_dryrun server_url = app.config.confluence_server_url space_key = app.config.confluence_space_key parent_ref = app.config.confluence_parent_page # initialize the publisher (if permitted) if app.config.confluence_publish: process_ask_configs(app.config) publisher = ConfluencePublisher() publisher.init(app.config) except Exception: sys.stdout.flush() logger.error(traceback.format_exc()) if os.path.isfile(os.path.join(work_dir, 'conf.py')): logger.error('unable to load configuration') else: logger.error('no documentation/missing configuration') return 1 if not publisher: logger.error('publishing not configured in sphinx configuration') return 1 if args.parent and not parent_ref: logger.error('parent option provided but no parent page is configured') return 1 # reminder warning print('') sys.stdout.flush() logger.warn('!!! DANGER DANGER DANGER !!!') print(""" A request has been made to attempt to wipe the pages from a configured Confluence instance. This action is not reversible with this tool and may require assistance from an administrator from a Confluence instance to recover pages. Only use this action if you know what you are doing. """) sys.stdout.flush() logger.warn('!!! DANGER DANGER DANGER !!!') print('') if not ask_question('Are you sure you want to continue?'): return 0 print('') # user has confirmed; start an attempt to wipe publisher.connect() base_page_id = None if args.parent: base_page_id = publisher.get_base_page_id() if aggressive_search: legacy_pages = publisher.get_descendants_compat(base_page_id) else: legacy_pages = publisher.get_descendants(base_page_id) print(' URL:', server_url) print(' Space:', space_key) if base_page_id: logger.note(' Pages: Child pages of ' + parent_ref) else: logger.note(' Pages: All Pages') print(' Total pages:', len(legacy_pages)) if dryrun: print(' Dry run:', 'Enabled (no pages will be removed)') if not legacy_pages: print('') print('No pages detected on this space. Exiting...') return 0 if args.verbose: print('-------------------------') page_names = [] for p in legacy_pages: page_names.append(publisher._name_cache[p]) sorted(page_names) print('\n'.join(page_names)) print('-------------------------') print('') if not ask_question('Are you sure you want to REMOVE these pages?'): return 0 print('') logger.info('Removing pages...', nonl=True) if dryrun: logger.info('') for page_id in legacy_pages: publisher.remove_page(page_id) if not dryrun: logger.info('.', nonl=True) if not dryrun: logger.info(__('done')) return 0