def test_list_accounts( mocker, yaml_load_fixture_auth, os_path_exists_fixture, # pylint: disable=unused-argument os_path_isfile_fixture): # pylint: disable=unused-argument """ List all accounts that have been configured Given - Auth file contains multiple accounts When - list_auth is called Then - All the configured account credentials are listed """ copy_typical_auth_contents = copy.deepcopy(TYPICAL_AUTH_CONTENTS) yaml_load_fixture_auth.return_value = copy_typical_auth_contents client = AuthConfigurationClient() with mocker.patch('f5cli.config.core.open', new_callable=mocker.mock_open()): result = client.list_auth() assert len(result) == 5 for index in range(0, 4): assert result[index].get('name') \ == TYPICAL_AUTH_CONTENTS[index].get('name')
def test_update_non_existing_account( mocker, yaml_load_fixture_auth, os_path_exists_fixture, # pylint: disable=unused-argument os_path_isfile_fixture): # pylint: disable=unused-argument """ Update non-existing account Given - Auth file containing multiple accounts When - store_auth('update') is invoked with auth details Then - store_auth raises an exception when trying to update a non existing account """ new_account = {'name': 'bigip4'} copy_typical_auth_contents = copy.deepcopy(TYPICAL_AUTH_CONTENTS) yaml_load_fixture_auth.return_value = copy_typical_auth_contents client = AuthConfigurationClient(auth=new_account) with pytest.raises(click.exceptions.ClickException) as error: with mocker.patch('f5cli.config.auth.open', new_callable=mocker.mock_open()): client.store_auth('update') assert error.value.args[0] == f"Update command failed." \ f" A account of bigip4 name does not exist."
def test_write_nonexist_config_directory(mocker, os_path_exists_fixture, os_path_isfile_fixture): """ Write Auth file into a non-exist directory Given - Config directory does not exist When - store_auth() is invoked Then - Config directory is created - Auth file is written to disk """ mock_path_exist = os_path_exists_fixture mock_path_exist.return_value = False mock_path_is_file = os_path_isfile_fixture mock_path_is_file.return_value = False mock_make_dir = mocker.patch("f5cli.config.core.os.makedirs") auth = {'name': 'blah', 'username': '******', 'password': '******'} client = AuthConfigurationClient(auth=auth) mock_yaml_safe_dump = mocker.patch("f5cli.config.core.yaml.safe_dump") with mocker.patch('f5cli.config.core.open', new_callable=mocker.mock_open()): client.store_auth('create') mock_yaml_safe_dump.assert_called_once() mock_make_dir.assert_called_once() assert mock_yaml_safe_dump.call_args_list[0][0][0][0] == auth
def test_create_pre_existing_account( mocker, yaml_load_fixture_auth, os_path_exists_fixture, # pylint: disable=unused-argument os_path_isfile_fixture): # pylint: disable=unused-argument """ Attempt to perform crate auth account when an auth account of the same name has already been configured Given - Auth file containing multiple accounts When - store_auth('create') is invoked with auth details Then - store_auth raises an exception when trying to create a pre-existing account """ new_account = {'name': 'cs1'} copy_typical_auth_contents = copy.deepcopy(TYPICAL_AUTH_CONTENTS) yaml_load_fixture_auth.return_value = copy_typical_auth_contents client = AuthConfigurationClient(auth=new_account) with pytest.raises(click.exceptions.ClickException) as error: with mocker.patch('f5cli.config.core.open', new_callable=mocker.mock_open()): client.store_auth('create') assert error.value.args[0] == f"Create command failed. " \ f"A account of cs1 name already exists."
def auth_update(ctx, # pylint: disable=too-many-arguments authentication_provider, host, port, name, set_default, api_endpoint, user, password): """ command """ auth_info = { 'name': name, 'authentication-type': authentication_provider, 'default': set_default, 'host': host, 'port': port, 'api-endpoint': api_endpoint } if user: if password is None: password = click.prompt("Password", type=str, hide_input=True) auth_info.update({ 'user': user, 'password': password}) auth_client = AuthConfigurationClient(auth=auth_info) auth_client.store_auth('update') ctx.log('Authentication updated successfully')
def test_read_exist_auth( mocker, yaml_load_fixture_auth, os_path_exists_fixture, # pylint: disable=unused-argument os_path_isfile_fixture): # pylint: disable=unused-argument """ Read an existing Auth file Given - Auth file exists When - read_auth() is invoked Then - The appropriate credentials are returned """ client = AuthConfigurationClient() yaml_load_fixture_auth.return_value = [{ 'username': '******', 'authentication-type': 'cs', 'password': '******', 'default': True }] with mocker.patch('f5cli.config.auth.open', new_callable=mocker.mock_open()): result = client.read_auth('cs') assert result == yaml_load_fixture_auth.return_value[0] yaml_load_fixture_auth.assert_called_once()
def test_read_auth_without_key( mocker, yaml_load_fixture_auth, os_path_exists_fixture, # pylint: disable=unused-argument os_path_isfile_fixture): # pylint: disable=unused-argument """ Attempt to read an Auth file that exists, but does not contain the required key Given - Auth file does exist - 'Group' key does not exist When - read_auth() is invoked Then - Exception is thrown """ client = AuthConfigurationClient() group_name = 'CS' yaml_load_fixture_auth.return_value = [{ 'name': 'temp', 'username': '******', 'type': 'BIGIP', 'password': '******' }] with pytest.raises(click.exceptions.ClickException) as error: with mocker.patch('f5cli.config.auth.open', new_callable=mocker.mock_open()): client.read_auth(group_name) assert error.value.args[0] == f"Command failed. You must configure a default " \ f"authentication for {group_name}!"
def auth_delete(ctx, name): """ command """ auth_client = AuthConfigurationClient() auth_client.delete_auth(name) ctx.log(f"Successfully deleted auth: {name} contents")
def auth_create(ctx, # pylint: disable=too-many-arguments authentication_provider, host, port, name, set_default, api_endpoint, user, password): """ command """ auth_info = { 'name': name, 'authentication-type': authentication_provider, 'default': set_default } if user: if password is None: password = click.prompt("Password", type=str, hide_input=True) auth_info.update({ 'user': user, 'password': password}) if authentication_provider == \ constants.AUTHENTICATION_PROVIDERS.get(constants.CLOUD_SERVICES_GROUP_NAME): if api_endpoint is not None: auth_info['api_endpoint'] = api_endpoint else: if host is not None: auth_info['host'] = host auth_info['port'] = port auth_client = AuthConfigurationClient(auth=auth_info) auth_client.store_auth('create') ctx.log('Authentication configured successfully')
def test_deleting_non_exist_account( mocker, yaml_load_fixture_auth, os_path_exists_fixture, # pylint: disable=unused-argument os_path_isfile_fixture): # pylint: disable=unused-argument """ Delete a account that is hasn't been configured Given - Auth file contains multiple accounts - The account name to be deleted that does not exist in the auth file When - delete_auth(account_name) is called Then - an exception should be thrown """ copy_typical_auth_contents = copy.deepcopy(TYPICAL_AUTH_CONTENTS) yaml_load_fixture_auth.return_value = copy_typical_auth_contents client = AuthConfigurationClient() with pytest.raises(click.exceptions.ClickException) as error: with mocker.patch('f5cli.config.auth.open', new_callable=mocker.mock_open()): client.delete_auth('blah') assert error.value.args[ 0] == f"Delete command failed. No account named blah found"
def test_deleting_default_account( mocker, yaml_load_fixture_auth, os_path_exists_fixture, # pylint: disable=unused-argument os_path_isfile_fixture): # pylint: disable=unused-argument """ Delete a account that is configiured as a default account Given - Auth file contains multiple accounts - The account name to be deleted exists in the auth file and is a default account When - delete_auth(account_name) is called Then - The next available account of the same authentication provider is updated to be the new default account and the specified account is removed """ mock_yaml_safe_dump = mocker.patch("f5cli.config.core.yaml.safe_dump") copy_typical_auth_contents = copy.deepcopy(TYPICAL_AUTH_CONTENTS) yaml_load_fixture_auth.return_value = copy_typical_auth_contents client = AuthConfigurationClient() with mocker.patch('f5cli.config.core.open', new_callable=mocker.mock_open()): client.delete_auth('bigip2') mock_yaml_safe_dump.assert_called_once() mock_yaml_dump_wrote = mock_yaml_safe_dump.call_args_list[0][0][0] assert mock_yaml_dump_wrote[1].get('default') assert mock_yaml_dump_wrote[3].get('name') == 'bigip3'
def auth_delete(ctx, name, auto_approve): """ command """ approval_confirmation_map = { 'delete': 'Auth contents named %s will be deleted.' % name } verify_approval('delete', approval_confirmation_map, auto_approve) auth_client = AuthConfigurationClient() auth_client.delete_auth(name) ctx.log(f"Successfully deleted auth: {name} contents")
def get_mgmt_client(): """ Get Management Client """ auth_client = AuthConfigurationClient() auth = auth_client.read_auth(constants.AUTHENTICATION_PROVIDERS['BIGIP']) management_kwargs = dict(port=auth['port'], user=auth['user'], password=auth['password']) return ManagementClient(auth['host'], **management_kwargs)
def get_mgmt_client(): """ Get Management Client """ auth_client = AuthConfigurationClient() auth = auth_client.read_auth(constants.AUTHENTICATION_PROVIDERS['CS']) management_kwargs = dict(user=auth['user'], password=auth['password'], api_endpoint=auth.pop('api_endpoint', None)) return ManagementClient(**management_kwargs)
def test_write_to_existing_auth_file( mocker, yaml_load_fixture_auth, os_path_exists_fixture, # pylint: disable=unused-argument os_path_isfile_fixture): # pylint: disable=unused-argument """ Write credentials to an existing Auth file Given - Config directory exists - Auth file exists with contents - Create action is invoked When - store_auth('create') is invoked Then - The existing Auth file is read - The Auth file is overwritten, containing merged credentials """ bigip_auth = { 'name': 'bigip_auth', 'username': '******', 'password': '******', 'host': '1.2.3.4', 'type': 'BIGIP', 'default': 'true' } client = AuthConfigurationClient(auth=bigip_auth) cs_auth = { 'name': 'cs_auth', 'username': '******', 'password': '******', 'type': 'CS', 'default': 'true' } yaml_load_fixture_auth.return_value = [cs_auth] mock_yaml_safe_dump = mocker.patch("f5cli.config.core.yaml.safe_dump") with mocker.patch('f5cli.config.core.open', new_callable=mocker.mock_open()): client.store_auth('create') mock_yaml_safe_dump.assert_called_once() mock_yaml_dump_wrote = mock_yaml_safe_dump.call_args_list[0][0][0] assert mock_yaml_dump_wrote[0] == cs_auth assert mock_yaml_dump_wrote[1] == bigip_auth
def subscription(ctx, action, subscription_id, declaration): """ Performs actions against a F5 Cloud Services subscription Parameters ---------- action : str which action to perform subscription_id : str which subscription to perform the requested action on declaration : str file name or path to file of declaration to send to F5 Cloud Services Returns ------- str the response for the requested action against the F5 Cloud Services subscription """ # Additional option validation if action == 'update' and declaration is None: raise click.ClickException( 'The --declaration option is required when updating a Cloud Services subscription' ) auth = AuthConfigurationClient().read_auth( constants.AUTHENTICATION_PROVIDERS[ constants.CLOUD_SERVICES_GROUP_NAME]) mgmt_client = ManagementClient(user=auth['user'], password=auth['password'], api_endpoint=auth.pop('api_endpoint', None)) subscription_client = SubscriptionClient(mgmt_client) if action == 'show': ctx.log(subscription_client.show(name=subscription_id)) elif action == 'update': ctx.log( subscription_client.update( name=subscription_id, config_file=utils_core.convert_to_absolute(declaration))) else: raise click.ClickException( f"Action {action} not implemented for 'subscription' command")
def test_read_nonexist_auth( os_path_exists_fixture, # pylint: disable=unused-argument os_path_isfile_fixture): # pylint: disable=unused-argument """ Attempt to read an Auth file that does not exist Given - Auth file does not exist When - read_auth() is invoked Then - Exception is thrown """ client = AuthConfigurationClient() mock_path_is_file = os_path_isfile_fixture mock_path_is_file.return_value = False with pytest.raises(click.exceptions.ClickException) as error: client.read_auth('temp') assert error.value.args[0] == "Command failed. " \ "You must configure a default authentication for temp!"
def cli(ctx, authentication_provider, host, port, api_endpoint, user, password): """ command """ if authentication_provider == \ constants.AUTHENTICATION_PROVIDERS.get(constants.BIGIP_GROUP_NAME): if host is None: host = click.prompt("Host", type=str) auth_info = { 'name': BIGIP_AUTH_ACCOUNT_NAME, 'authentication-type': authentication_provider, 'default': True, 'host': host, 'port': port } else: auth_info = { 'name': CS_AUTH_ACCOUNT_NAME, 'authentication-type': authentication_provider, 'default': True } if api_endpoint is not None: auth_info['api_endpoint'] = api_endpoint if user is None: user = click.prompt("User", type=str) auth_info['user'] = user if password is None: password = click.prompt("Password", type=str, hide_input=True) auth_info['password'] = password # Validate credentials if authentication_provider == \ constants.AUTHENTICATION_PROVIDERS.get(constants.BIGIP_GROUP_NAME): try: management_kwargs = dict(port=auth_info['port'], user=auth_info['user'], password=auth_info['password']) BigipManagementClient(auth_info['host'], **management_kwargs) except (DeviceReadyError, HTTPError) as error: raise click.ClickException(f"Failed to login to BIG-IP: {error}") else: try: CSManagementClient(user=auth_info['user'], password=auth_info['password'], api_endpoint=auth_info.pop( 'api_endpoint', None)) except HTTPError as error: raise click.ClickException( f"Failed to login to Cloud Services: {error}") # Store credentials in auth file auth_client = AuthConfigurationClient(auth=auth_info) try: auth_client.store_auth('create') except click.ClickException: auth_client.store_auth('update') ctx.log('Logged in successfully')
def test_update_existing_account( mocker, yaml_load_fixture_auth, os_path_exists_fixture, # pylint: disable=unused-argument os_path_isfile_fixture): # pylint: disable=unused-argument """ Update an existing account Given - Auth file contains multiple accounts - The account name to the updated exists in the auth file When - store_auth('update') Then - Auth file is updated with the new account and the default account for the specific authentication provider is updated """ new_account = {'name': 'bigip3', 'host': 'host2', 'default': True} mock_yaml_safe_dump = mocker.patch("f5cli.config.core.yaml.safe_dump") copy_typical_auth_contents = copy.deepcopy(TYPICAL_AUTH_CONTENTS) yaml_load_fixture_auth.return_value = copy_typical_auth_contents client = AuthConfigurationClient(auth=new_account) with mocker.patch('f5cli.config.core.open', new_callable=mocker.mock_open()): client.store_auth('update') mock_yaml_safe_dump.assert_called_once() mock_yaml_dump_wrote = mock_yaml_safe_dump.call_args_list[0][0][0] assert not mock_yaml_dump_wrote[3].get('default') returned_account = mock_yaml_dump_wrote[4] expected_result = TYPICAL_AUTH_CONTENTS[4] assert returned_account.get('host') == new_account.get('host') assert returned_account.get('default') == new_account.get('default') assert returned_account.get('user') == expected_result.get('user') assert returned_account.get('password') == expected_result.get( 'password') assert returned_account.get('type') == expected_result.get('type')
def test_write_exist_config_directory( mocker, os_path_exists_fixture, # pylint: disable=unused-argument os_path_isfile_fixture): """ Write credentials to Auth file in an existing directory Given - Config directory exist - Auth file does not exist When - store_auth() is invoked Then - The credentials are written to the Auth file """ client = AuthConfigurationClient(auth={'name': 'blah'}) mock_path_isfile = os_path_isfile_fixture mock_path_isfile.return_value = False mock_yaml_safe_dump = mocker.patch("f5cli.config.core.yaml.safe_dump") with mocker.patch('f5cli.config.core.open', new_callable=mocker.mock_open()): client.store_auth('create') mock_yaml_safe_dump.assert_called_once()
def test_update_no_auth_file( mocker, yaml_load_fixture_auth, os_path_exists_fixture, # pylint: disable=unused-argument os_path_isfile_fixture): # pylint: disable=unused-argument """ Attempt to perform update auth account when no auth accounts have been configured yet Given - No Auth file When - store_auth('update') is invoked with auth details Then - store_auth raises an exception """ new_account = {'name': 'bigip4'} yaml_load_fixture_auth.return_value = None client = AuthConfigurationClient(auth=new_account) with pytest.raises(click.exceptions.ClickException) as error: with mocker.patch('f5cli.config.auth.open', new_callable=mocker.mock_open()): client.store_auth('update') assert error.value.args[0] == f"Update command failed. " \ f"No accounts have been configured yet"
def service(ctx, action, component, version, declaration, install_component): """ command """ auth = AuthConfigurationClient().read_auth(constants.AUTHENTICATION_PROVIDERS['BIGIP']) management_kwargs = dict(port=auth['port'], user=auth['user'], password=auth['password']) client = ManagementClient(auth['host'], **management_kwargs) kwargs = {} if version: kwargs['version'] = version extension_client = ExtensionClient(client, component, **kwargs) # intent based - support install in 'service' sub-command # install extension component if requested (and not installed) if install_component and not extension_client.package.is_installed()['installed']: extension_client.package.install() extension_client.service.is_available() try: if action == 'show': ctx.log(extension_client.service.show()) elif action == 'create': ctx.log(_process_create(component, extension_client, declaration)) elif action == 'delete': ctx.log(extension_client.service.delete()) elif action == 'show-info': ctx.log(extension_client.service.show_info()) elif action == 'show-failover': ctx.log(extension_client.service.show_trigger()) elif action == 'trigger-failover': ctx.log(extension_client.service.trigger( config_file=utils_core.convert_to_absolute(declaration))) elif action == 'show-inspect': ctx.log(extension_client.service.show_inspect()) elif action == 'reset': ctx.log(extension_client.service.reset( config_file=utils_core.convert_to_absolute(declaration))) else: raise click.ClickException('Action not implemented') except Exception as error: raise click.ClickException(error)
def package(ctx, action, component, version, use_latest_metadata): """ command """ auth = AuthConfigurationClient().read_auth(constants.AUTHENTICATION_PROVIDERS['BIGIP']) management_kwargs = dict(port=auth['port'], user=auth['user'], password=auth['password']) client = ManagementClient(auth['host'], **management_kwargs) kwargs = {} if version: kwargs['version'] = version if use_latest_metadata: kwargs['use_latest_metadata'] = use_latest_metadata if action == 'verify': ctx.log(extension_operations.verify_package(client, component, **kwargs)) elif action == 'install': ctx.log(extension_operations.install_package(client, component, **kwargs)) elif action == 'uninstall': ctx.log(extension_operations.uninstall_package(client, component, **kwargs)) elif action == 'upgrade': ctx.log(extension_operations.upgrade_package(client, component, version)) else: raise click.ClickException('Action {} not implemented'.format(action))
def auth_list(ctx): """ command """ ctx.log(AuthConfigurationClient().list_auth())