diff --git a/README.md b/README.md index 20c46d50..d5c39820 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,7 @@ You can see the current configuration by running: ```sh $ deadline config show ``` -and change the settings by running the associated `get` and `set` commands. +and change the settings by running the associated `get`, `set` and `clear` commands. To see a list of settings that can be configured, run: ```sh diff --git a/src/deadline/client/cli/_groups/config_group.py b/src/deadline/client/cli/_groups/config_group.py index cea0b261..60514883 100644 --- a/src/deadline/client/cli/_groups/config_group.py +++ b/src/deadline/client/cli/_groups/config_group.py @@ -78,6 +78,19 @@ def config_set(setting_name, value): config_file.set_setting(setting_name, value) +@cli_config.command(name="clear") +@click.argument("setting_name") +@_handle_error +def config_clear(setting_name): + """ + Sets a workstation configuration setting back to the default value. + + For example `deadline config clear defaults.farm_id`. + Run `deadline config --help` to show available settings. + """ + config_file.clear_setting(setting_name) + + @cli_config.command(name="get") @click.argument("setting_name") @_handle_error diff --git a/src/deadline/client/config/__init__.py b/src/deadline/client/config/__init__.py index 8b49ed2a..ccacb056 100644 --- a/src/deadline/client/config/__init__.py +++ b/src/deadline/client/config/__init__.py @@ -11,6 +11,7 @@ "get_setting_default", "get_setting", "set_setting", + "clear_setting", "get_best_profile_for_farm", "str2bool", "DEFAULT_DEADLINE_ENDPOINT_URL", @@ -22,5 +23,6 @@ get_setting, get_setting_default, set_setting, + clear_setting, str2bool, ) diff --git a/src/deadline/client/config/config_file.py b/src/deadline/client/config/config_file.py index e47ec354..b7336c77 100644 --- a/src/deadline/client/config/config_file.py +++ b/src/deadline/client/config/config_file.py @@ -8,6 +8,7 @@ "get_setting_default", "get_setting", "set_setting", + "clear_setting", "get_best_profile_for_farm", "str2bool", ] @@ -420,6 +421,20 @@ def set_setting(setting_name: str, value: str, config: Optional[ConfigParser] = write_config(config) +def clear_setting(setting_name: str, config: Optional[ConfigParser] = None): + """ + Sets the value of the specified setting back to the default value. + + Args: + setting_name (str): The full setting name, like `section.setting`. + config (Optional[ConfigParser]): If provided clears the setting in the parser and does not save to disk. + """ + if "." not in setting_name: + raise DeadlineOperationError(f"The setting name {setting_name!r} is not valid.") + + set_setting(setting_name, get_setting_default(setting_name, config=config), config=config) + + def get_best_profile_for_farm(farm_id: str, queue_id: Optional[str] = None) -> str: """ Finds the best AWS profile for the specified farm and queue IDs. Chooses diff --git a/test/unit/deadline_client/cli/test_cli_config.py b/test/unit/deadline_client/cli/test_cli_config.py index 6b0eb2dc..db1c4c56 100644 --- a/test/unit/deadline_client/cli/test_cli_config.py +++ b/test/unit/deadline_client/cli/test_cli_config.py @@ -147,22 +147,43 @@ def test_config_settings_via_cli_roundtrip( assert result.exit_code == 0 assert result.output.strip() == str(alternate_value) + result = runner.invoke(main, ["config", "clear", setting_name]) + assert result.exit_code == 0 + assert result.output.strip() == "" + + result = runner.invoke(main, ["config", "get", setting_name]) + assert result.exit_code == 0 + assert result.output.strip() == str(default_value) -def test_config_get_setting_nonexistant(fresh_deadline_config): + +def test_config_set_setting_nonexistant(fresh_deadline_config): """Test that we get an error with a non-existent setting.""" runner = CliRunner() - result = runner.invoke(main, ["config", "get", "settings.doesnt_exist"]) + result = runner.invoke(main, ["config", "set", "settings.doesnt_exist", "value"]) assert result.exit_code == 1 assert "doesnt_exist" in result.output -def test_config_set_setting_nonexistant(fresh_deadline_config): +@pytest.mark.parametrize( + "config_command", + [ + pytest.param( + "get", + id="config get nonexistant setting", + ), + pytest.param( + "clear", + id="config clear nonexistant setting", + ), + ], +) +def test_config_command_setting_nonexistant(config_command, fresh_deadline_config): """Test that we get an error with a non-existent setting.""" runner = CliRunner() - result = runner.invoke(main, ["config", "set", "settings.doesnt_exist", "value"]) + result = runner.invoke(main, ["config", config_command, "settings.doesnt_exist"]) assert result.exit_code == 1 assert "doesnt_exist" in result.output diff --git a/test/unit/deadline_client/config/test_config_file.py b/test/unit/deadline_client/config/test_config_file.py index d813f825..b94eff16 100644 --- a/test/unit/deadline_client/config/test_config_file.py +++ b/test/unit/deadline_client/config/test_config_file.py @@ -56,7 +56,7 @@ def test_config_settings_hierarchy(fresh_deadline_config): assert config.get_setting("settings.storage_profile_id") == "" # Switch back to the default profile, and check the next layer of the onion - config.set_setting("defaults.aws_profile_name", "(default)") + config.clear_setting("defaults.aws_profile_name") assert config.get_setting("defaults.farm_id") == "farm-for-profile-default" # The queue id is still default assert config.get_setting("defaults.queue_id") == "" @@ -64,13 +64,13 @@ def test_config_settings_hierarchy(fresh_deadline_config): assert config.get_setting("settings.storage_profile_id") == "" # Switch back to the default farm - config.set_setting("defaults.farm_id", "") + config.clear_setting("defaults.farm_id") assert config.get_setting("defaults.queue_id") == "queue-for-farm-default" # Storage profile needs "profile - farm_id" so it should be back to the original assert config.get_setting("settings.storage_profile_id") == "storage-profile-for-farm-default" # Switch to default farm and default queue - config.set_setting("defaults.queue_id", "") + config.clear_setting("defaults.queue_id") assert config.get_setting("settings.storage_profile_id") == "storage-profile-for-farm-default" @@ -116,6 +116,27 @@ def test_config_set_setting_nonexistant(fresh_deadline_config): assert "aws_porfile_name" in str(excinfo.value) +def test_config_clear_setting_nonexistant(fresh_deadline_config): + """Test the error from clear_setting when a setting doesn't exist.""" + # Setting name without the '.' + with pytest.raises(DeadlineOperationError) as excinfo: + config.clear_setting("setting_name_bad_format") + assert "is not valid" in str(excinfo.value) + assert "setting_name_bad_format" in str(excinfo.value) + + # Section name is wrong + with pytest.raises(DeadlineOperationError) as excinfo: + config.clear_setting("setitngs.aws_profile_name") + assert "has no setting" in str(excinfo.value) + assert "setitngs" in str(excinfo.value) + + # Section is good, but no setting + with pytest.raises(DeadlineOperationError) as excinfo: + config.clear_setting("settings.aws_porfile_name") + assert "has no setting" in str(excinfo.value) + assert "aws_porfile_name" in str(excinfo.value) + + @patch.object(config_file, "_should_read_config", MagicMock(return_value=True)) def test_config_file_env_var(fresh_deadline_config): """Test that setting the env var DEADLINE_CONFIG_FILE_PATH overrides the config path"""