def make_decentralized_ursulas(ursula_config: UrsulaConfiguration, stakers_addresses: Iterable[str], workers_addresses: Iterable[str], commit_to_next_period: bool = False, **ursula_overrides) -> List[Ursula]: if not MOCK_KNOWN_URSULAS_CACHE: starting_port = MOCK_URSULA_STARTING_PORT else: starting_port = max(MOCK_KNOWN_URSULAS_CACHE.keys()) + 1 stakers_and_workers = zip(stakers_addresses, workers_addresses) ursulas = list() for port, (staker_address, worker_address) in enumerate(stakers_and_workers, start=starting_port): ursula = ursula_config.produce(checksum_address=staker_address, worker_address=worker_address, db_filepath=MOCK_URSULA_DB_FILEPATH, rest_port=port + 100, **ursula_overrides) if commit_to_next_period: ursula.transacting_power.activate() ursula.commit_to_next_period() ursulas.append(ursula) # Store this Ursula in our global cache. port = ursula.rest_interface.port MOCK_KNOWN_URSULAS_CACHE[port] = ursula return ursulas
def test_ursula_development_configuration(federated_only=True): config = UrsulaConfiguration(dev_mode=True, federated_only=federated_only) assert config.is_me is True assert config.dev_mode is True assert config.keyring == NO_KEYRING_ATTACHED # Produce an Ursula ursula_one = config() # Ensure we do in fact have an Ursula here assert isinstance(ursula_one, Ursula) assert len(ursula_one.checksum_address) == 42 assert ursula_one.federated_only is federated_only # A Temporary Ursula port = ursula_one.rest_information()[0].port assert port == UrsulaConfiguration.DEFAULT_DEVELOPMENT_REST_PORT assert tempfile.gettempdir() in ursula_one.datastore.engine.url.database assert ursula_one.certificate_filepath is CERTIFICATE_NOT_SAVED assert UrsulaConfiguration.TEMP_CONFIGURATION_DIR_PREFIX in ursula_one.keyring_root assert isinstance(ursula_one.node_storage, ForgetfulNodeStorage) assert ursula_one.node_storage._name == ":memory:" # Alternate way to produce a character with a direct call ursula_two = config.produce() assert isinstance(ursula_two, Ursula) # All development Ursulas are unique ursulas = [ursula_one, ursula_two] for _ in range(3): ursula = config() assert ursula not in ursulas ursulas.append(ursula)
def make_federated_ursulas(ursula_config: UrsulaConfiguration, quantity: int = NUMBER_OF_URSULAS_IN_DEVELOPMENT_NETWORK, know_each_other: bool = True, **ursula_overrides) -> Set[Ursula]: if not MOCK_KNOWN_URSULAS_CACHE: starting_port = MOCK_URSULA_STARTING_PORT else: starting_port = max(MOCK_KNOWN_URSULAS_CACHE.keys()) + 1 federated_ursulas = set() for port in range(starting_port, starting_port+quantity): ursula = ursula_config.produce(rest_port=port + 100, db_filepath=MOCK_URSULA_DB_FILEPATH, **ursula_overrides) federated_ursulas.add(ursula) # Store this Ursula in our global testing cache. port = ursula.rest_interface.port MOCK_KNOWN_URSULAS_CACHE[port] = ursula if know_each_other: for ursula_to_teach in federated_ursulas: # Add other Ursulas as known nodes. for ursula_to_learn_about in federated_ursulas: ursula_to_teach.remember_node(ursula_to_learn_about) return federated_ursulas
def make_decentralized_ursulas(ursula_config: UrsulaConfiguration, blockchain: BlockchainInterface, stakers_addresses: Iterable[str], workers_addresses: Iterable[str], confirm_activity: bool = False, **ursula_overrides) -> List[Ursula]: if not MOCK_KNOWN_URSULAS_CACHE: starting_port = MOCK_URSULA_STARTING_PORT else: starting_port = max(MOCK_KNOWN_URSULAS_CACHE.keys()) + 1 stakers_and_workers = zip(stakers_addresses, workers_addresses) ursulas = list() stake_tracker = StakeTracker(checksum_addresses=list(stakers_addresses)) for port, (staker_address, worker_address) in enumerate(stakers_and_workers, start=starting_port): ursula = ursula_config.produce(checksum_address=staker_address, worker_address=worker_address, blockchain=blockchain, db_filepath=MOCK_URSULA_DB_FILEPATH, rest_port=port + 100, stake_tracker=stake_tracker, **ursula_overrides) if confirm_activity: ursula.confirm_activity() ursulas.append(ursula) # Store this Ursula in our global cache. port = ursula.rest_interface.port MOCK_KNOWN_URSULAS_CACHE[port] = ursula return ursulas
def make_decentralized_ursulas(ursula_config: UrsulaConfiguration, stakers_addresses: Iterable[str], workers_addresses: Iterable[str], commit_to_next_period: bool = False, **ursula_overrides) -> List[Ursula]: if not MOCK_KNOWN_URSULAS_CACHE: starting_port = MOCK_URSULA_STARTING_PORT else: starting_port = max(MOCK_KNOWN_URSULAS_CACHE.keys()) + 1 stakers_and_workers = zip(stakers_addresses, workers_addresses) ursulas = list() for port, (staker_address, worker_address) in enumerate(stakers_and_workers, start=starting_port): ursula = ursula_config.produce(checksum_address=staker_address, worker_address=worker_address, db_filepath=tempfile.mkdtemp(), rest_port=port + 100, **ursula_overrides) if commit_to_next_period: # TODO: Is _crypto_power trying to be public? Or is there a way to expose *something* public about TransactingPower? # Do we need to revisit the concept of "public material"? Or does this rightly belong as a method? tx_power = ursula._crypto_power.power_ups(TransactingPower) tx_power.activate() ursula.commit_to_next_period() ursulas.append(ursula) # Store this Ursula in our global cache. port = ursula.rest_interface.port MOCK_KNOWN_URSULAS_CACHE[port] = ursula return ursulas
def run_ursula(rest_port, rest_host, db_name, checksum_address, federated_only, metadata_dir, config_file, dev ) -> None: """ The following procedure is required to "spin-up" an Ursula node. 1. Initialize UrsulaConfiguration 2. Initialize Ursula 3. Run TLS deployment 4. Start the staking daemon Configurable values are first read from the configuration file, but can be overridden (mostly for testing purposes) with inline cli options. """ if not dev: click.echo("WARNING: Development mode is disabled") temp = False else: click.echo("Running in development mode") temp = True if config_file: ursula_config = UrsulaConfiguration.from_configuration_file(filepath=config_file) else: ursula_config = UrsulaConfiguration(temp=temp, auto_initialize=temp, rest_host=rest_host, rest_port=rest_port, db_name=db_name, is_me=True, federated_only=federated_only, checksum_address=checksum_address, # save_metadata=False, # TODO load_metadata=True, known_metadata_dir=metadata_dir, start_learning_now=True, abort_on_learning_error=temp) try: URSULA = ursula_config.produce() URSULA.get_deployer().run() # Run TLS Deploy (Reactor) if not URSULA.federated_only: # TODO: Resume / Init URSULA.stake() # Start Staking Daemon finally: click.echo("Cleaning up temporary runtime files and directories") ursula_config.cleanup() # TODO: Integrate with other "graceful" shutdown functionality click.echo("Exited gracefully")
def make_decentralized_ursulas(ursula_config: UrsulaConfiguration, ether_addresses: Union[list, int], stake: bool = False, know_each_other: bool = True, **ursula_overrides) -> Set[Ursula]: # Alternately accepts an int of the quantity of ursulas to make if isinstance(ether_addresses, int): ether_addresses = [ to_checksum_address(secure_random(20)) for _ in range(ether_addresses) ] if not TEST_KNOWN_URSULAS_CACHE: starting_port = TEST_URSULA_STARTING_PORT else: starting_port = max(TEST_KNOWN_URSULAS_CACHE.keys()) + 1 ursulas = set() for port, checksum_address in enumerate(ether_addresses, start=starting_port): ursula = ursula_config.produce(checksum_address=checksum_address, db_name="test-{}".format(port), rest_port=port + 100, **ursula_overrides) if stake is True: min_stake, balance = int( constants.MIN_ALLOWED_LOCKED), ursula.token_balance amount = random.randint(min_stake, balance) # for a random lock duration min_locktime, max_locktime = int( constants.MIN_LOCKED_PERIODS), int( constants.MAX_MINTING_PERIODS) periods = random.randint(min_locktime, max_locktime) ursula.initialize_stake(amount=amount, lock_periods=periods) ursulas.add(ursula) # Store this Ursula in our global cache. port = ursula.rest_information()[0].port TEST_KNOWN_URSULAS_CACHE[port] = ursula if know_each_other: for ursula_to_teach in ursulas: # Add other Ursulas as known nodes. for ursula_to_learn_about in ursulas: ursula_to_teach.remember_node(ursula_to_learn_about) return ursulas
def make_decentralized_ursulas(ursula_config: UrsulaConfiguration, ether_addresses: Union[list, int], stake: bool = False, know_each_other: bool = True, economics: TokenEconomics = None, **ursula_overrides) -> List[Ursula]: if not economics: economics = TokenEconomics() # Alternately accepts an int of the quantity of ursulas to make if isinstance(ether_addresses, int): ether_addresses = [to_checksum_address(secure_random(20)) for _ in range(ether_addresses)] if not MOCK_KNOWN_URSULAS_CACHE: starting_port = MOCK_URSULA_STARTING_PORT else: starting_port = max(MOCK_KNOWN_URSULAS_CACHE.keys()) + 1 ursulas = list() for port, checksum_address in enumerate(ether_addresses, start=starting_port): ursula = ursula_config.produce(checksum_public_address=checksum_address, db_filepath=MOCK_URSULA_DB_FILEPATH, rest_port=port + 100, **ursula_overrides) if stake is True: min_stake, balance = economics.minimum_allowed_locked, ursula.token_balance amount = random.randint(min_stake, balance) # for a random lock duration min_locktime, max_locktime = economics.minimum_locked_periods, economics.maximum_locked_periods periods = random.randint(min_locktime, max_locktime) ursula.initialize_stake(amount=amount, lock_periods=periods) ursulas.append(ursula) # Store this Ursula in our global cache. port = ursula.rest_information()[0].port MOCK_KNOWN_URSULAS_CACHE[port] = ursula if know_each_other: for ursula_to_teach in ursulas: # Add other Ursulas as known nodes. for ursula_to_learn_about in ursulas: ursula_to_teach.remember_node(ursula_to_learn_about) return ursulas
def test_federated_ursula_development_configuration(): # Configure & Produce Ursula ursula_config = UrsulaConfiguration(dev_mode=True, federated_only=True) ursula = ursula_config.produce() # Network Port port = ursula.rest_information()[0].port assert port == UrsulaConfiguration.DEFAULT_DEVELOPMENT_REST_PORT # Database assert tempfile.gettempdir() in ursula.datastore.engine.url.database # TLS Certificate assert ursula.certificate_filepath is CERTIFICATE_NOT_SAVED
def make_decentralized_ursulas(ursula_config: UrsulaConfiguration, staking_provider_addresses: Iterable[str], operator_addresses: Iterable[str], commit_now=True, **ursula_overrides) -> List[Ursula]: if not MOCK_KNOWN_URSULAS_CACHE: starting_port = MOCK_URSULA_STARTING_PORT else: starting_port = max(MOCK_KNOWN_URSULAS_CACHE.keys()) + 1 providers_and_operators = zip(staking_provider_addresses, operator_addresses) ursulas = list() for port, (staking_provider_address, operator_address) in enumerate(providers_and_operators, start=starting_port): ursula = ursula_config.produce( checksum_address=staking_provider_address, operator_address=operator_address, db_filepath=MOCK_DB, rest_port=port + 100, **ursula_overrides) # TODO: Confirm operator here? # if commit_now: # ursula.confirm_operator_address() ursulas.append(ursula) # Store this Ursula in our global testing cache. port = ursula.rest_interface.port MOCK_KNOWN_URSULAS_CACHE[port] = ursula return ursulas
def ursula( click_config, action, debug, dev, quiet, dry_run, force, lonely, network, teacher_uri, min_stake, rest_host, rest_port, db_filepath, checksum_address, federated_only, poa, config_root, config_file, metadata_dir, # TODO: Start nodes from an additional existing metadata dir provider_uri, no_registry, registry_filepath) -> None: """ Manage and run an "Ursula" PRE node. \b Actions ------------------------------------------------- \b run Run an "Ursula" node. init Create a new Ursula node configuration. view View the Ursula node's configuration. forget Forget all known nodes. save-metadata Manually write node metadata to disk without running destroy Delete Ursula node configuration. """ # # Boring Setup Stuff # if not quiet: click.secho(URSULA_BANNER) log = Logger('ursula.cli') if debug and quiet: raise click.BadOptionUsage( option_name="quiet", message="--debug and --quiet cannot be used at the same time.") if debug: click_config.log_to_sentry = False click_config.log_to_file = True globalLogPublisher.removeObserver(logToSentry) # Sentry GlobalConsoleLogger.set_log_level("debug") elif quiet: globalLogPublisher.removeObserver(logToSentry) globalLogPublisher.removeObserver(SimpleObserver) globalLogPublisher.removeObserver(getJsonFileObserver()) # # Pre-Launch Warnings # if not quiet: if dev: click.secho("WARNING: Running in development mode", fg='yellow') if force: click.secho("WARNING: Force is enabled", fg='yellow') # # Unauthenticated Configurations # if action == "init": """Create a brand-new persistent Ursula""" if dev and not quiet: click.secho("WARNING: Using temporary storage area", fg='yellow') if not config_root: # Flag config_root = click_config.config_file # Envvar if not rest_host: rest_host = click.prompt( "Enter Ursula's public-facing IPv4 address") ursula_config = UrsulaConfiguration.generate( password=click_config.get_password(confirm=True), config_root=config_root, rest_host=rest_host, rest_port=rest_port, db_filepath=db_filepath, domains={network} if network else None, federated_only=federated_only, checksum_public_address=checksum_address, no_registry=federated_only or no_registry, registry_filepath=registry_filepath, provider_uri=provider_uri, poa=poa) if not quiet: click.secho("Generated keyring {}".format( ursula_config.keyring_dir), fg='green') click.secho("Saved configuration file {}".format( ursula_config.config_file_location), fg='green') # Give the use a suggestion as to what to do next... how_to_run_message = "\nTo run an Ursula node from the default configuration filepath run: \n\n'{}'\n" suggested_command = 'nucypher ursula run' if config_root is not None: config_file_location = os.path.join( config_root, config_file or UrsulaConfiguration.CONFIG_FILENAME) suggested_command += ' --config-file {}'.format( config_file_location) click.secho(how_to_run_message.format(suggested_command), fg='green') return # FIN else: click.secho("OK") elif action == "destroy": """Delete all configuration files from the disk""" if dev: message = "'nucypher ursula destroy' cannot be used in --dev mode" raise click.BadOptionUsage(option_name='--dev', message=message) destroy_system_configuration(config_class=UrsulaConfiguration, config_file=config_file, network=network, config_root=config_root, force=force, log=log) if not quiet: click.secho("Destroyed {}".format(config_root)) return # Development Configuration if dev: ursula_config = UrsulaConfiguration( dev_mode=True, domains={TEMPORARY_DOMAIN}, poa=poa, registry_filepath=registry_filepath, provider_uri=provider_uri, checksum_public_address=checksum_address, federated_only=federated_only, rest_host=rest_host, rest_port=rest_port, db_filepath=db_filepath) # Authenticated Configurations else: # Deserialize network domain name if override passed if network: domain_constant = getattr(constants, network.upper()) domains = {domain_constant} else: domains = None ursula_config = UrsulaConfiguration.from_configuration_file( filepath=config_file, domains=domains, registry_filepath=registry_filepath, provider_uri=provider_uri, rest_host=rest_host, rest_port=rest_port, db_filepath=db_filepath, # TODO: Handle Boolean overrides # poa=poa, # federated_only=federated_only, ) try: # Unlock Keyring if not quiet: click.secho('Decrypting keyring...', fg='blue') ursula_config.keyring.unlock(password=click_config.get_password() ) # Takes ~3 seconds, ~1GB Ram except CryptoError: raise ursula_config.keyring.AuthenticationFailed if not ursula_config.federated_only: try: ursula_config.connect_to_blockchain(recompile_contracts=False) ursula_config.connect_to_contracts() except EthereumContractRegistry.NoRegistry: message = "Cannot configure blockchain character: No contract registry found; " \ "Did you mean to pass --federated-only?" raise EthereumContractRegistry.NoRegistry(message) click_config.ursula_config = ursula_config # Pass Ursula's config onto staking sub-command # # Launch Warnings # if not quiet: if ursula_config.federated_only: click.secho("WARNING: Running in Federated mode", fg='yellow') # # Action Switch # if action == 'run': """Seed, Produce, Run!""" # # Seed - Step 1 # teacher_nodes = list() if teacher_uri: node = Ursula.from_teacher_uri( teacher_uri=teacher_uri, min_stake=min_stake, federated_only=ursula_config.federated_only) teacher_nodes.append(node) # # Produce - Step 2 # ursula = ursula_config(known_nodes=teacher_nodes, lonely=lonely) # GO! try: # # Run - Step 3 # click.secho("Connecting to {}".format(','.join( str(d) for d in ursula_config.domains)), fg='blue', bold=True) click.secho("Running Ursula {} on {}".format( ursula, ursula.rest_interface), fg='green', bold=True) if not debug: stdio.StandardIO(UrsulaCommandProtocol(ursula=ursula)) if dry_run: # That's all folks! return ursula.get_deployer().run() # <--- Blocking Call (Reactor) except Exception as e: ursula_config.log.critical(str(e)) click.secho("{} {}".format(e.__class__.__name__, str(e)), fg='red') raise # Crash :-( finally: if not quiet: click.secho("Stopping Ursula") ursula_config.cleanup() if not quiet: click.secho("Ursula Stopped", fg='red') return elif action == "save-metadata": """Manually save a node self-metadata file""" ursula = ursula_config.produce(ursula_config=ursula_config) metadata_path = ursula.write_node_metadata(node=ursula) if not quiet: click.secho("Successfully saved node metadata to {}.".format( metadata_path), fg='green') return elif action == "view": """Paint an existing configuration to the console""" paint_configuration( config_filepath=config_file or ursula_config.config_file_location) return elif action == "forget": """Forget all known nodes via storages""" click.confirm("Permanently delete all known node data?", abort=True) ursula_config.forget_nodes() message = "Removed all stored node node metadata and certificates" click.secho(message=message, fg='red') return else: raise click.BadArgumentUsage("No such argument {}".format(action))
def ursula( click_config, action, dev, quiet, dry_run, force, lonely, network, teacher_uri, min_stake, rest_host, rest_port, db_filepath, checksum_address, federated_only, poa, config_root, config_file, metadata_dir, # TODO: Start nodes from an additional existing metadata dir provider_uri, recompile_solidity, no_registry, registry_filepath) -> None: """ Manage and run an "Ursula" PRE node. \b Actions ------------------------------------------------- \b run Run an "Ursula" node. init Create a new Ursula node configuration. view View the Ursula node's configuration. forget Forget all known nodes. save-metadata Manually write node metadata to disk without running destroy Delete Ursula node configuration. """ # # Boring Setup Stuff # if not quiet: log = Logger('ursula.cli') if click_config.debug and quiet: raise click.BadOptionUsage( option_name="quiet", message="--debug and --quiet cannot be used at the same time.") if click_config.debug: click_config.log_to_sentry = False click_config.log_to_file = True globalLogPublisher.removeObserver(logToSentry) # Sentry GlobalConsoleLogger.set_log_level("debug") elif quiet: globalLogPublisher.removeObserver(logToSentry) globalLogPublisher.removeObserver(SimpleObserver) globalLogPublisher.removeObserver(getJsonFileObserver()) if not click_config.json_ipc and not click_config.quiet: click.secho(URSULA_BANNER) # # Pre-Launch Warnings # if not quiet: if dev: click.secho("WARNING: Running in development mode", fg='yellow') if force: click.secho("WARNING: Force is enabled", fg='yellow') # # Unauthenticated Configurations # if action == "init": """Create a brand-new persistent Ursula""" if not network: raise click.BadArgumentUsage( '--network is required to initialize a new configuration.') if dev: actions.handle_control_output( message="WARNING: Using temporary storage area", color='yellow', quiet=quiet, json=click_config.json) if not config_root: # Flag config_root = click_config.config_file # Envvar if not rest_host: rest_host = click.prompt( "Enter Ursula's public-facing IPv4 address" ) # TODO: Remove this step ursula_config = UrsulaConfiguration.generate( password=click_config.get_password(confirm=True), config_root=config_root, rest_host=rest_host, rest_port=rest_port, db_filepath=db_filepath, domains={network} if network else None, federated_only=federated_only, checksum_public_address=checksum_address, no_registry=federated_only or no_registry, registry_filepath=registry_filepath, provider_uri=provider_uri, poa=poa) click_config.emitter(message="Generated keyring {}".format( ursula_config.keyring_dir), color='green') click_config.emitter(message="Saved configuration file {}".format( ursula_config.config_file_location), color='green') # Give the use a suggestion as to what to do next... how_to_run_message = "\nTo run an Ursula node from the default configuration filepath run: \n\n'{}'\n" suggested_command = 'nucypher ursula run' if config_root is not None: config_file_location = os.path.join( config_root, config_file or UrsulaConfiguration.CONFIG_FILENAME) suggested_command += ' --config-file {}'.format( config_file_location) return click_config.emitter( message=how_to_run_message.format(suggested_command), color='green') # Development Configuration if dev: ursula_config = UrsulaConfiguration( dev_mode=True, domains={TEMPORARY_DOMAIN}, poa=poa, registry_filepath=registry_filepath, provider_uri=provider_uri, checksum_public_address=checksum_address, federated_only=federated_only, rest_host=rest_host, rest_port=rest_port, db_filepath=db_filepath) # Authenticated Configurations else: # Deserialize network domain name if override passed if network: domain_constant = getattr(constants, network.upper()) domains = {domain_constant} else: domains = None ursula_config = UrsulaConfiguration.from_configuration_file( filepath=config_file, domains=domains, registry_filepath=registry_filepath, provider_uri=provider_uri, rest_host=rest_host, rest_port=rest_port, db_filepath=db_filepath, # TODO: Handle Boolean overrides # poa=poa, # federated_only=federated_only, ) actions.unlock_keyring(configuration=ursula_config, password=click_config.get_password()) if not ursula_config.federated_only: actions.connect_to_blockchain(configuration=ursula_config, recompile_contracts=recompile_solidity) click_config.ursula_config = ursula_config # Pass Ursula's config onto staking sub-command # # Launch Warnings # if ursula_config.federated_only: click_config.emitter(message="WARNING: Running in Federated mode", color='yellow') # # Action Switch # if action == 'run': """Seed, Produce, Run!""" # # Seed - Step 1 # teacher_uris = [teacher_uri] if teacher_uri else list() teacher_nodes = actions.load_seednodes( teacher_uris=teacher_uris, min_stake=min_stake, federated_only=federated_only, network_middleware=click_config.middleware) # # Produce - Step 2 # URSULA = ursula_config(known_nodes=teacher_nodes, lonely=lonely) # GO! try: # # Run - Step 3 # click_config.emitter(message="Connecting to {}".format(','.join( str(d) for d in ursula_config.domains)), color='green', bold=True) click_config.emitter(message="Running Ursula {} on {}".format( URSULA, URSULA.rest_interface), color='green', bold=True) if not click_config.debug: stdio.StandardIO(UrsulaCommandProtocol(ursula=URSULA)) if dry_run: # That's all folks! return URSULA.get_deployer().run() # <--- Blocking Call (Reactor) except Exception as e: ursula_config.log.critical(str(e)) click_config.emitter(message="{} {}".format( e.__class__.__name__, str(e)), color='red', bold=True) raise # Crash :-( finally: click_config.emitter(message="Stopping Ursula", color='green') ursula_config.cleanup() click_config.emitter(message="Ursula Stopped", color='red') return elif action == "save-metadata": """Manually save a node self-metadata file""" URSULA = ursula_config.produce(ursula_config=ursula_config) metadata_path = ursula.write_node_metadata(node=URSULA) return click_config.emitter( message="Successfully saved node metadata to {}.".format( metadata_path), color='green') elif action == "view": """Paint an existing configuration to the console""" response = UrsulaConfiguration._read_configuration_file( filepath=config_file or ursula_config.config_file_location) return click_config.emitter(response=response) elif action == "forget": # TODO: Move to character control actions.forget(configuration=ursula_config) return elif action == "destroy": """Delete all configuration files from the disk""" if dev: message = "'nucypher ursula destroy' cannot be used in --dev mode" raise click.BadOptionUsage(option_name='--dev', message=message) destroyed_filepath = destroy_system_configuration( config_class=UrsulaConfiguration, config_file=config_file, network=network, config_root=ursula_config.config_file_location, force=force) return click_config.emitter(message=f"Destroyed {destroyed_filepath}", color='green') else: raise click.BadArgumentUsage("No such argument {}".format(action))