Skip to content

fix: raise error when -r resource filter matches no resources#372

Merged
endavis merged 1 commit intomainfrom
fix/367-resource-filter-silent-success
Mar 14, 2026
Merged

fix: raise error when -r resource filter matches no resources#372
endavis merged 1 commit intomainfrom
fix/367-resource-filter-silent-success

Conversation

@endavis
Copy link
Owner

@endavis endavis commented Mar 13, 2026

Description

When using infra plan/apply/destroy -r <name>, if the resource names don't match any loaded resources, the command silently reports success without warning. This fix adds validation to raise a ResourceFilterError when zero resources match, and prints a warning when only some resources match (partial match).

Addresses #367

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • Test improvement

Changes Made

  • Added ResourceFilterError exception to src/infrafoundry/core/exceptions.py
  • Added validation in _iter_provider_batches() (orchestrator.py): zero matches raises error with available resource names, partial matches prints warning
  • Added _validate_resource_filter() helper to DeploymentExecutor (deployment_executor.py), called from apply_serial() and apply_parallel()
  • Added IF-VALIDATION-005 error catalog entry in cli/errors.py
  • Added 12 new tests covering zero match, partial match, full match, and no filter scenarios

Testing

  • All existing tests pass
  • Added new tests for new functionality
  • Manually tested the changes

Checklist

  • My code follows the code style of this project (ran doit format)
  • I have run linting checks (doit lint)
  • I have run type checking (doit type_check)
  • I have added tests that prove my fix is effective or that my feature works
  • All new and existing tests pass (doit test)
  • My changes generate no new warnings

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link

⚠️ API Breaking Changes Detected

The following breaking changes were detected by griffe:

src/infrafoundry/cli/errors.py:84: ERROR_CATALOG:
Attribute value was changed:
  Old: {'IF-CONFIG-001': ErrorInfo(code='IF-CONFIG-001', title='Environment Not Found', description='The specified environment does not exist in the configuration.', suggestions=["Run 'foundry config envs' to list available environments", 'Check that the environment name is spelled correctly', 'Verify INFRAFOUNDRY_CONFIG_REPO points to the correct directory'], see_also='docs/configuration/environments.md'), 'IF-CONFIG-002': ErrorInfo(code='IF-CONFIG-002', title='Invalid Configuration', description='The configuration file contains invalid or malformed data.', suggestions=["Run 'foundry config validate --env <env>' to see validation errors", 'Check YAML syntax (indentation, colons, quotes)', 'Ensure required fields are present'], see_also='docs/configuration/syntax.md'), 'IF-CONFIG-003': ErrorInfo(code='IF-CONFIG-003', title='Missing Configuration', description='A required configuration file or value is missing.', suggestions=['Check that all required configuration files exist', "Run 'foundry config new --env <env>' to create a new environment", 'Verify the config directory structure matches expected layout']), 'IF-PROVIDER-001': ErrorInfo(code='IF-PROVIDER-001', title='Provider Not Found', description='The requested provider is not registered or available.', suggestions=['Check that the provider name is spelled correctly', 'Verify the provider plugin is installed', 'Check provider configuration in your environment']), 'IF-PROVIDER-002': ErrorInfo(code='IF-PROVIDER-002', title='Provider Initialization Failed', description='The provider failed to initialize properly.', suggestions=['Check provider credentials and configuration', 'Verify network connectivity to the provider', 'Check provider-specific environment variables']), 'IF-PROVIDER-003': ErrorInfo(code='IF-PROVIDER-003', title='Unsupported Resource Type', description='The provider does not support this resource type.', suggestions=['Check the provider documentation for supported resources', 'Verify the resource type name is correct', 'Consider using a different provider for this resource']), 'IF-RUNNER-001': ErrorInfo(code='IF-RUNNER-001', title='Terraform Execution Failed', description='Terraform command failed during execution.', suggestions=['Check the Terraform output above for specific errors', 'Verify Terraform is installed and accessible', "Run 'terraform init' in the generated directory", 'Check provider credentials are set correctly']), 'IF-RUNNER-002': ErrorInfo(code='IF-RUNNER-002', title='Ansible Execution Failed', description='Ansible playbook failed during execution.', suggestions=['Check the Ansible output for specific task failures', 'Verify SSH connectivity to target hosts', 'Check that required Ansible collections are installed']), 'IF-RUNNER-003': ErrorInfo(code='IF-RUNNER-003', title='Deployment Failed', description='The deployment operation failed.', suggestions=['Check the error output for specific failure reasons', 'Verify all prerequisites are met', 'Try running with --debug for more details']), 'IF-RUNNER-004': ErrorInfo(code='IF-RUNNER-004', title='Rollback Failed', description='The rollback operation failed.', suggestions=['Check the state database for valid rollback points', 'Verify the target deployment ID exists', 'Try manual rollback using the generated files']), 'IF-STATE-001': ErrorInfo(code='IF-STATE-001', title='State Operation Failed', description='A state management operation failed.', suggestions=['Check the state database is accessible', 'Verify file permissions on the state directory', "Run 'foundry state init' to reinitialize the state"]), 'IF-STATE-002': ErrorInfo(code='IF-STATE-002', title='Deployment Not Found', description='The requested deployment does not exist in state.', suggestions=["Run 'foundry state list' to see available deployments", 'Check that the deployment ID is correct', "Verify you're using the correct environment"]), 'IF-STATE-003': ErrorInfo(code='IF-STATE-003', title='Resource Not Found', description='The requested resource does not exist in state.', suggestions=["Run 'foundry state resources' to list tracked resources", 'Verify the resource name is correct', 'Check if the resource was previously deleted']), 'IF-STATE-004': ErrorInfo(code='IF-STATE-004', title='State Inconsistency', description='The state database is inconsistent with actual resources.', suggestions=["Run 'foundry infra drift --env <env>' to detect drift", "Consider running 'foundry state reset' (use with caution)", 'Check for concurrent modifications to infrastructure']), 'IF-CREDENTIAL-001': ErrorInfo(code='IF-CREDENTIAL-001', title='Missing Credentials', description='Required credentials are not configured.', suggestions=['Set the required environment variables', 'Check the secrets/ directory for credential files', 'Verify SOPS/age keys are configured for encrypted secrets']), 'IF-CREDENTIAL-002': ErrorInfo(code='IF-CREDENTIAL-002', title='Invalid Credentials', description='The provided credentials are invalid or malformed.', suggestions=['Verify credential values are correct', 'Check for typos in environment variable names', 'Ensure API keys/tokens have not expired']), 'IF-SECRET-001': ErrorInfo(code='IF-SECRET-001', title='Secret Not Found', description='The requested secret does not exist.', suggestions=['Check that the secret file exists in secrets/<env>/', 'Verify the secret name is spelled correctly', "Run 'foundry secrets init' to set up secrets directory"]), 'IF-SECRET-002': ErrorInfo(code='IF-SECRET-002', title='Secret Decryption Failed', description='Failed to decrypt the secret.', suggestions=['Verify SOPS_AGE_KEY_FILE is set correctly', 'Check that your age key matches the encrypted secret', 'Ensure the secret file is properly encrypted with SOPS']), 'IF-POLICY-001': ErrorInfo(code='IF-POLICY-001', title='Policy Violation', description='The operation violates one or more policies.', suggestions=['Review the policy violations listed above', 'Modify resources to comply with policies', 'Contact your administrator if policies need updating']), 'IF-POLICY-002': ErrorInfo(code='IF-POLICY-002', title='Policy Not Found', description='The specified policy does not exist.', suggestions=["Run 'foundry policy list' to see available policies", 'Check that the policy name is correct', 'Verify policies/ directory exists in config repo']), 'IF-VALIDATION-001': ErrorInfo(code='IF-VALIDATION-001', title='Validation Failed', description='Resource validation failed.', suggestions=['Review the validation errors listed above', 'Check resource configuration against schema', "Run 'foundry config validate' for detailed validation"]), 'IF-VALIDATION-002': ErrorInfo(code='IF-VALIDATION-002', title='Connectivity Validation Failed', description='Provider connectivity check failed.', suggestions=['Verify network connectivity to the provider', 'Check firewall rules and security groups', 'Ensure provider API endpoints are accessible']), 'IF-VALIDATION-003': ErrorInfo(code='IF-VALIDATION-003', title='Reference Validation Failed', description='A resource reference is invalid.', suggestions=['Check that referenced resources exist', 'Verify resource names match exactly (case-sensitive)', "Review dependency graph with 'foundry analyze dependencies'"]), 'IF-VALIDATION-004': ErrorInfo(code='IF-VALIDATION-004', title='Schema Validation Failed', description='Data does not match the expected schema.', suggestions=['Review the schema requirements in documentation', 'Check for missing required fields', 'Verify data types match expected schema']), 'IF-DEPENDENCY-001': ErrorInfo(code='IF-DEPENDENCY-001', title='Circular Dependency Detected', description='Resources have circular dependencies that cannot be resolved.', suggestions=["Run 'foundry analyze graph --env <env>' to visualize dependencies", 'Break the circular dependency by restructuring resources', 'Use explicit dependency ordering if needed']), 'IF-DEPENDENCY-002': ErrorInfo(code='IF-DEPENDENCY-002', title='Missing Dependency', description='A required dependency is not available.', suggestions=['Check that dependent resources are defined', 'Verify resource names in depends_on are correct', 'Ensure dependencies are in the same environment']), 'IF-API-001': ErrorInfo(code='IF-API-001', title='API Request Failed', description='An API request to an external service failed.', suggestions=['Check network connectivity', 'Verify API credentials are valid', 'Check if the service is experiencing issues']), 'IF-API-002': ErrorInfo(code='IF-API-002', title='Connection Failed', description='Failed to connect to the remote service.', suggestions=['Verify the service URL is correct', 'Check network connectivity and firewall rules', 'Ensure the service is running and accessible']), 'IF-API-003': ErrorInfo(code='IF-API-003', title='Authentication Failed', description='API authentication failed.', suggestions=['Verify your API credentials are correct', 'Check if credentials have expired', 'Ensure the API key has required permissions']), 'IF-API-004': ErrorInfo(code='IF-API-004', title='Request Timeout', description='The API request timed out.', suggestions=['Try the operation again', 'Check if the service is overloaded', 'Consider increasing timeout settings']), 'IF-TEMPLATE-001': ErrorInfo(code='IF-TEMPLATE-001', title='Template Rendering Failed', description='Failed to render a configuration template.', suggestions=['Check template syntax for errors', 'Verify all required variables are provided', 'Review Jinja2 template documentation']), 'IF-PLUGIN-001': ErrorInfo(code='IF-PLUGIN-001', title='Plugin Discovery Failed', description='Failed to discover plugins.', suggestions=['Check that plugins are properly installed', 'Verify entry point configuration', 'Reinstall the package if needed']), 'IF-PLUGIN-002': ErrorInfo(code='IF-PLUGIN-002', title='Plugin Load Failed', description='Failed to load a plugin.', suggestions=['Check plugin dependencies are installed', 'Verify plugin version compatibility', 'Check for import errors in the plugin']), 'IF-PLUGIN-003': ErrorInfo(code='IF-PLUGIN-003', title='Plugin Validation Failed', description='Plugin validation failed.', suggestions=['Ensure plugin implements required interface', 'Check plugin configuration', 'Review plugin documentation']), 'IF-SYSTEM-001': ErrorInfo(code='IF-SYSTEM-001', title='File Not Found', description='A required file or directory does not exist.', suggestions=['Check that the file path is correct', 'Verify the file exists and is readable', "Make sure you're running from the correct directory"]), 'IF-SYSTEM-002': ErrorInfo(code='IF-SYSTEM-002', title='Permission Denied', description='Insufficient permissions to access a file or resource.', suggestions=['Check file permissions', 'Try running with appropriate access rights', 'Verify the file is not locked by another process']), 'IF-SYSTEM-003': ErrorInfo(code='IF-SYSTEM-003', title='IO Error', description='An input/output operation failed.', suggestions=['Check disk space availability', 'Verify the storage device is working properly', 'Check for file system errors']), 'IF-SYSTEM-004': ErrorInfo(code='IF-SYSTEM-004', title='Network Error', description='A network operation failed.', suggestions=['Check your network connection', 'Verify the remote service is reachable', 'Check firewall and proxy settings']), 'IF-GENERAL-001': ErrorInfo(code='IF-GENERAL-001', title='Unknown Error', description='An unexpected error occurred.', suggestions=['Run with --debug for full error details', 'Check the logs for more information', 'Report the issue if it persists'])}
  New: {'IF-CONFIG-001': ErrorInfo(code='IF-CONFIG-001', title='Environment Not Found', description='The specified environment does not exist in the configuration.', suggestions=["Run 'foundry config envs' to list available environments", 'Check that the environment name is spelled correctly', 'Verify INFRAFOUNDRY_CONFIG_REPO points to the correct directory'], see_also='docs/configuration/environments.md'), 'IF-CONFIG-002': ErrorInfo(code='IF-CONFIG-002', title='Invalid Configuration', description='The configuration file contains invalid or malformed data.', suggestions=["Run 'foundry config validate --env <env>' to see validation errors", 'Check YAML syntax (indentation, colons, quotes)', 'Ensure required fields are present'], see_also='docs/configuration/syntax.md'), 'IF-CONFIG-003': ErrorInfo(code='IF-CONFIG-003', title='Missing Configuration', description='A required configuration file or value is missing.', suggestions=['Check that all required configuration files exist', "Run 'foundry config new --env <env>' to create a new environment", 'Verify the config directory structure matches expected layout']), 'IF-PROVIDER-001': ErrorInfo(code='IF-PROVIDER-001', title='Provider Not Found', description='The requested provider is not registered or available.', suggestions=['Check that the provider name is spelled correctly', 'Verify the provider plugin is installed', 'Check provider configuration in your environment']), 'IF-PROVIDER-002': ErrorInfo(code='IF-PROVIDER-002', title='Provider Initialization Failed', description='The provider failed to initialize properly.', suggestions=['Check provider credentials and configuration', 'Verify network connectivity to the provider', 'Check provider-specific environment variables']), 'IF-PROVIDER-003': ErrorInfo(code='IF-PROVIDER-003', title='Unsupported Resource Type', description='The provider does not support this resource type.', suggestions=['Check the provider documentation for supported resources', 'Verify the resource type name is correct', 'Consider using a different provider for this resource']), 'IF-RUNNER-001': ErrorInfo(code='IF-RUNNER-001', title='Terraform Execution Failed', description='Terraform command failed during execution.', suggestions=['Check the Terraform output above for specific errors', 'Verify Terraform is installed and accessible', "Run 'terraform init' in the generated directory", 'Check provider credentials are set correctly']), 'IF-RUNNER-002': ErrorInfo(code='IF-RUNNER-002', title='Ansible Execution Failed', description='Ansible playbook failed during execution.', suggestions=['Check the Ansible output for specific task failures', 'Verify SSH connectivity to target hosts', 'Check that required Ansible collections are installed']), 'IF-RUNNER-003': ErrorInfo(code='IF-RUNNER-003', title='Deployment Failed', description='The deployment operation failed.', suggestions=['Check the error output for specific failure reasons', 'Verify all prerequisites are met', 'Try running with --debug for more details']), 'IF-RUNNER-004': ErrorInfo(code='IF-RUNNER-004', title='Rollback Failed', description='The rollback operation failed.', suggestions=['Check the state database for valid rollback points', 'Verify the target deployment ID exists', 'Try manual rollback using the generated files']), 'IF-STATE-001': ErrorInfo(code='IF-STATE-001', title='State Operation Failed', description='A state management operation failed.', suggestions=['Check the state database is accessible', 'Verify file permissions on the state directory', "Run 'foundry state init' to reinitialize the state"]), 'IF-STATE-002': ErrorInfo(code='IF-STATE-002', title='Deployment Not Found', description='The requested deployment does not exist in state.', suggestions=["Run 'foundry state list' to see available deployments", 'Check that the deployment ID is correct', "Verify you're using the correct environment"]), 'IF-STATE-003': ErrorInfo(code='IF-STATE-003', title='Resource Not Found', description='The requested resource does not exist in state.', suggestions=["Run 'foundry state resources' to list tracked resources", 'Verify the resource name is correct', 'Check if the resource was previously deleted']), 'IF-STATE-004': ErrorInfo(code='IF-STATE-004', title='State Inconsistency', description='The state database is inconsistent with actual resources.', suggestions=["Run 'foundry infra drift --env <env>' to detect drift", "Consider running 'foundry state reset' (use with caution)", 'Check for concurrent modifications to infrastructure']), 'IF-CREDENTIAL-001': ErrorInfo(code='IF-CREDENTIAL-001', title='Missing Credentials', description='Required credentials are not configured.', suggestions=['Set the required environment variables', 'Check the secrets/ directory for credential files', 'Verify SOPS/age keys are configured for encrypted secrets']), 'IF-CREDENTIAL-002': ErrorInfo(code='IF-CREDENTIAL-002', title='Invalid Credentials', description='The provided credentials are invalid or malformed.', suggestions=['Verify credential values are correct', 'Check for typos in environment variable names', 'Ensure API keys/tokens have not expired']), 'IF-SECRET-001': ErrorInfo(code='IF-SECRET-001', title='Secret Not Found', description='The requested secret does not exist.', suggestions=['Check that the secret file exists in secrets/<env>/', 'Verify the secret name is spelled correctly', "Run 'foundry secrets init' to set up secrets directory"]), 'IF-SECRET-002': ErrorInfo(code='IF-SECRET-002', title='Secret Decryption Failed', description='Failed to decrypt the secret.', suggestions=['Verify SOPS_AGE_KEY_FILE is set correctly', 'Check that your age key matches the encrypted secret', 'Ensure the secret file is properly encrypted with SOPS']), 'IF-POLICY-001': ErrorInfo(code='IF-POLICY-001', title='Policy Violation', description='The operation violates one or more policies.', suggestions=['Review the policy violations listed above', 'Modify resources to comply with policies', 'Contact your administrator if policies need updating']), 'IF-POLICY-002': ErrorInfo(code='IF-POLICY-002', title='Policy Not Found', description='The specified policy does not exist.', suggestions=["Run 'foundry policy list' to see available policies", 'Check that the policy name is correct', 'Verify policies/ directory exists in config repo']), 'IF-VALIDATION-001': ErrorInfo(code='IF-VALIDATION-001', title='Validation Failed', description='Resource validation failed.', suggestions=['Review the validation errors listed above', 'Check resource configuration against schema', "Run 'foundry config validate' for detailed validation"]), 'IF-VALIDATION-002': ErrorInfo(code='IF-VALIDATION-002', title='Connectivity Validation Failed', description='Provider connectivity check failed.', suggestions=['Verify network connectivity to the provider', 'Check firewall rules and security groups', 'Ensure provider API endpoints are accessible']), 'IF-VALIDATION-003': ErrorInfo(code='IF-VALIDATION-003', title='Reference Validation Failed', description='A resource reference is invalid.', suggestions=['Check that referenced resources exist', 'Verify resource names match exactly (case-sensitive)', "Review dependency graph with 'foundry analyze dependencies'"]), 'IF-VALIDATION-004': ErrorInfo(code='IF-VALIDATION-004', title='Schema Validation Failed', description='Data does not match the expected schema.', suggestions=['Review the schema requirements in documentation', 'Check for missing required fields', 'Verify data types match expected schema']), 'IF-VALIDATION-005': ErrorInfo(code='IF-VALIDATION-005', title='Resource Filter Matched Nothing', description='The -r resource filter did not match any available resources.', suggestions=['Check spelling of the resource names passed to -r', "Run 'foundry infra list --env <env>' to see available resources", 'Verify the resources exist in your configuration']), 'IF-DEPENDENCY-001': ErrorInfo(code='IF-DEPENDENCY-001', title='Circular Dependency Detected', description='Resources have circular dependencies that cannot be resolved.', suggestions=["Run 'foundry analyze graph --env <env>' to visualize dependencies", 'Break the circular dependency by restructuring resources', 'Use explicit dependency ordering if needed']), 'IF-DEPENDENCY-002': ErrorInfo(code='IF-DEPENDENCY-002', title='Missing Dependency', description='A required dependency is not available.', suggestions=['Check that dependent resources are defined', 'Verify resource names in depends_on are correct', 'Ensure dependencies are in the same environment']), 'IF-API-001': ErrorInfo(code='IF-API-001', title='API Request Failed', description='An API request to an external service failed.', suggestions=['Check network connectivity', 'Verify API credentials are valid', 'Check if the service is experiencing issues']), 'IF-API-002': ErrorInfo(code='IF-API-002', title='Connection Failed', description='Failed to connect to the remote service.', suggestions=['Verify the service URL is correct', 'Check network connectivity and firewall rules', 'Ensure the service is running and accessible']), 'IF-API-003': ErrorInfo(code='IF-API-003', title='Authentication Failed', description='API authentication failed.', suggestions=['Verify your API credentials are correct', 'Check if credentials have expired', 'Ensure the API key has required permissions']), 'IF-API-004': ErrorInfo(code='IF-API-004', title='Request Timeout', description='The API request timed out.', suggestions=['Try the operation again', 'Check if the service is overloaded', 'Consider increasing timeout settings']), 'IF-TEMPLATE-001': ErrorInfo(code='IF-TEMPLATE-001', title='Template Rendering Failed', description='Failed to render a configuration template.', suggestions=['Check template syntax for errors', 'Verify all required variables are provided', 'Review Jinja2 template documentation']), 'IF-PLUGIN-001': ErrorInfo(code='IF-PLUGIN-001', title='Plugin Discovery Failed', description='Failed to discover plugins.', suggestions=['Check that plugins are properly installed', 'Verify entry point configuration', 'Reinstall the package if needed']), 'IF-PLUGIN-002': ErrorInfo(code='IF-PLUGIN-002', title='Plugin Load Failed', description='Failed to load a plugin.', suggestions=['Check plugin dependencies are installed', 'Verify plugin version compatibility', 'Check for import errors in the plugin']), 'IF-PLUGIN-003': ErrorInfo(code='IF-PLUGIN-003', title='Plugin Validation Failed', description='Plugin validation failed.', suggestions=['Ensure plugin implements required interface', 'Check plugin configuration', 'Review plugin documentation']), 'IF-SYSTEM-001': ErrorInfo(code='IF-SYSTEM-001', title='File Not Found', description='A required file or directory does not exist.', suggestions=['Check that the file path is correct', 'Verify the file exists and is readable', "Make sure you're running from the correct directory"]), 'IF-SYSTEM-002': ErrorInfo(code='IF-SYSTEM-002', title='Permission Denied', description='Insufficient permissions to access a file or resource.', suggestions=['Check file permissions', 'Try running with appropriate access rights', 'Verify the file is not locked by another process']), 'IF-SYSTEM-003': ErrorInfo(code='IF-SYSTEM-003', title='IO Error', description='An input/output operation failed.', suggestions=['Check disk space availability', 'Verify the storage device is working properly', 'Check for file system errors']), 'IF-SYSTEM-004': ErrorInfo(code='IF-SYSTEM-004', title='Network Error', description='A network operation failed.', suggestions=['Check your network connection', 'Verify the remote service is reachable', 'Check firewall and proxy settings']), 'IF-GENERAL-001': ErrorInfo(code='IF-GENERAL-001', title='Unknown Error', description='An unexpected error occurred.', suggestions=['Run with --debug for full error details', 'Check the logs for more information', 'Report the issue if it persists'])}

✅ Breaking Change Documented

This PR includes breaking change documentation.

Before merging, verify:

  • Migration guide in CHANGELOG.md
  • Documentation updated
  • Version will be bumped appropriately (major version)

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Details
Benchmark suite Current: 9d040ea Previous: 38393e3 Ratio
tests/benchmarks/test_placeholder.py::test_import_time 7107.90309174266 iter/sec (stddev: 0.000008500618715572725) 6834.338851237599 iter/sec (stddev: 0.000029672616249373274) 0.96

This comment was automatically generated by workflow using github-action-benchmark.

@endavis endavis added the ready-to-merge PR is reviewed and ready to merge label Mar 14, 2026
@endavis endavis merged commit 39059ca into main Mar 14, 2026
16 of 17 checks passed
@endavis endavis deleted the fix/367-resource-filter-silent-success branch March 14, 2026 09:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge PR is reviewed and ready to merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant