Skip to content

Plugin Routing Configuration Guide

Feature: Multi-Plugin Routing | Status: Stable | Since: v0.3.0

FinFocus intelligently routes cost calculation requests to the most appropriate plugins based on:

  1. Provider Matching: Automatically route resources to plugins that support their cloud provider
  2. Feature Capabilities: Route different operations (projected costs, recommendations) to specialized plugins
  3. Resource Patterns: Override default routing with custom glob or regex patterns
  4. Priority Ordering: Control which plugin is preferred when multiple match
  5. Automatic Fallback: Retry with alternative plugins when the primary fails

Layer 1: Automatic Provider-Based Routing (Zero Configuration)

Section titled “Layer 1: Automatic Provider-Based Routing (Zero Configuration)”

FinFocus automatically routes resources based on the SupportedProviders metadata each plugin reports:

Terminal window
# Install plugins - routing happens automatically
finfocus plugin install aws-public
finfocus plugin install gcp-public
# Verify what each plugin supports
finfocus plugin list
# NAME VERSION PROVIDERS
# aws-public 1.0.0 aws
# gcp-public 1.0.0 gcp
#
# Note: Output is tab-delimited. Column alignment may vary by terminal.
# Cost calculation automatically routes resources
finfocus cost projected --pulumi-json multi-cloud-plan.json
# aws:ec2/instance:Instance → aws-public (provider: aws)
# gcp:compute/instance:Instance → gcp-public (provider: gcp)

Provider Extraction: FinFocus takes the first segment of the resource type and maps it to the cloud that bills the resource. The provider is the cloud (aws, azure, gcp, kubernetes), not the Pulumi package or Terraform provider name:

Resource Type Provider
aws:ec2/instance:Instance aws
aws-native:ec2:Instance aws
gcp:compute/instance:Instance gcp
google-native:compute/v1:Instance gcp
azure:compute/virtualMachine:VirtualMachine azure
azure-native:compute:VirtualMachine azure
azurerm_linux_virtual_machine (Terraform) azure
google_compute_instance (Terraform) gcp
kubernetes:core/v1:Pod kubernetes

These names map to a cloud: aws-native to aws; azure-native and azurerm to azure; google-native and google to gcp. Any other prefix is used as written, lowercased. The resource type itself is never changed, so a plugin can still read the package from the type token, and a routing pattern such as aws-native:* still matches it. finfocus config routes test shows both, for example provider: aws (package: aws-native).

A plugin that reports aws receives aws-native resources. A plugin that still lists a package name, such as azure-native, keeps matching, and now also receives resources of the same cloud under another package name; it can decline them through Supports.

Cost reports, provider budgets, the provider= filter and history charts use the same cloud name, so aws and aws-native spend appears under one aws entry. Group by type to see packages separately.

Global Plugins: Plugins reporting ["*"] or empty providers match ALL resources (e.g., debugging plugins).

Metadata-Aware Binary Selection: When a plugin is installed with --metadata="region=us-west-2", the registry writes a plugin.metadata.json file alongside the binary. At load time the registry reads this metadata and prefers a region-specific binary (e.g., finfocus-plugin-aws-public-us-west-2) over the generic one. If no region binary is found, the standard binary is used as a fallback.

Layer 2: Declarative Configuration (Advanced Control)

Section titled “Layer 2: Declarative Configuration (Advanced Control)”

For advanced scenarios, add routing rules to ~/.finfocus/config.yaml:

routing:
plugins:
- name: aws-ce
features:
- Recommendations
priority: 20
fallback: true
- name: aws-public
features:
- ProjectedCosts
- ActualCosts
patterns:
- type: glob
pattern: 'aws:*'
priority: 10
fallback: true
routing:
plugins:
- name: <plugin-name> # Required: Installed plugin name
features: [...] # Optional: Limit to specific capabilities
patterns: [...] # Optional: Resource type patterns
priority: <number> # Optional: Selection priority (default: 0)
fallback: <bool> # Optional: Enable fallback (default: true)

The plugin identifier. Must match an installed plugin name from finfocus plugin list.

- name: aws-public

Limits which capabilities this plugin handles. Valid values:

  • ProjectedCosts - Monthly cost estimates
  • ActualCosts - Historical spending data
  • Recommendations - Cost optimization suggestions
  • Carbon - Carbon emission calculations
  • DryRun - What-if analysis
  • Budgets - Budget tracking

Empty/omitted: Plugin handles all features it reports.

- name: aws-ce
features:
- Recommendations
- ActualCosts

Resource type patterns that override automatic provider matching.

Empty/omitted: Use automatic provider-based routing.

patterns:
# Glob pattern (simpler syntax)
- type: glob
pattern: 'aws:eks:*'
# Regex pattern (full RE2 syntax)
- type: regex
pattern: 'aws:(ec2|rds)/.*'

Pattern Types:

Type Syntax Example Matches
glob *, ?, [...] aws:ec2:* aws:ec2:Instance, aws:ec2:SecurityGroup
regex RE2 regex aws:eks:.* aws:eks:Cluster, aws:eks:NodeGroup

Pattern Precedence: Patterns override automatic provider matching. If a pattern matches, the resource routes to that plugin regardless of provider.

Controls plugin selection order when multiple plugins match the same resource.

Default: 0 (all plugins queried in parallel)

Behavior:

  • Higher priority = preferred: priority: 30 beats priority: 10
  • Equal priority: Query all matching plugins in parallel
  • Priority 0: Special case - always query all matching
- name: eks-costs
priority: 30 # Highest - try first
- name: aws-ce
priority: 20 # Medium
- name: aws-public
priority: 10 # Lowest - fallback

Enables automatic retry with the next priority plugin if this one fails.

Default: true

Fallback Triggers:

  • Connection timeout
  • Plugin crash (EOF, connection reset)
  • Empty result (no cost data)
  • gRPC error (unavailable, internal)

NOT Fallback Triggers:

  • Validation error (InvalidArgument) - plugin explicitly rejected
  • $0 cost result - this is a valid result, not a failure
- name: aws-ce
fallback: true # Retry with next plugin on failure
- name: aws-public
fallback: false # Last resort - don't fallback

Automatic routing works perfectly for multi-cloud infrastructure:

routing:
plugins:
- name: aws-public
priority: 10
- name: gcp-public
priority: 10
- name: azure-public
priority: 10

Result: Each resource automatically routes to its provider’s plugin.

Route different features to specialized plugins:

routing:
plugins:
# AWS Cost Explorer for recommendations (uses AWS API)
- name: aws-ce
features:
- Recommendations
- ActualCosts
priority: 20
# AWS Public for projected costs (no credentials needed)
- name: aws-public
features:
- ProjectedCosts
priority: 10

Result:

  • finfocus cost projected → aws-public
  • finfocus cost recommendations → aws-ce
  • finfocus cost actual → aws-ce

Route specific resource types to specialized plugins:

routing:
plugins:
# EKS-specific plugin for Kubernetes costs
- name: eks-costs
patterns:
- type: glob
pattern: 'aws:eks:*'
priority: 30
# RDS-specific plugin for database costs
- name: rds-optimizer
patterns:
- type: regex
pattern: 'aws:rds:.*'
priority: 30
# General AWS plugin for everything else
- name: aws-public
priority: 10

Result:

  • aws:eks:Cluster → eks-costs (pattern match)
  • aws:rds:Instance → rds-optimizer (pattern match)
  • aws:ec2:Instance → aws-public (provider match)

Configure primary plugin with automatic fallback to backup:

routing:
plugins:
# Primary: Try AWS Cost Explorer first (most accurate)
- name: aws-ce
priority: 20
fallback: true
# Secondary: Try AWS Public as backup
- name: aws-public
priority: 10
fallback: true
# Tertiary: Local specs as last resort (automatic)

Execution Flow:

  1. Try aws-ce (priority 20)
  2. If fails and fallback: true → try aws-public (priority 10)
  3. If fails and fallback: true → try local specs (built-in)
  4. If no specs → return “no cost data available”

Note: Routing does not apply in analyzer/policy-pack mode

When finfocus runs as a Pulumi policy pack (pulumi preview --policy-pack), routing configuration is not consulted. All plugins discovered in the plugin directory are loaded and queried for every resource.

Additionally, global plugins (those with supported_providers: ["*"]) cannot be excluded via routing even in standard CLI mode — they are always included for all resources.

To exclude specific plugins when running as a policy pack, use FINFOCUS_HOME isolation. See Isolating plugins in analyzer mode.

Validate your routing configuration before using it:

Terminal window
finfocus config validate
# Success output:
✓ Configuration valid
Discovered plugins:
aws-ce: Recommendations, ActualCosts (priority: 20)
aws-public: ProjectedCosts (priority: 10)
Routing rules:
aws:eks:* → eks-costs (pattern, priority: 30)
aws:* → aws-public (provider, priority: 10)
# Error output:
✗ Configuration invalid
Errors:
- aws-ce: plugin not found (install with: finfocus plugin install aws-ce)
- patterns[0].pattern: invalid regex: missing closing bracket
Warnings:
- aws-public: feature 'Carbon' not supported by plugin

Validation happens automatically on first use. Invalid patterns are skipped with warnings logged.

See exactly which plugin was selected and why:

Terminal window
# Via flag
finfocus cost projected --debug --pulumi-json plan.json
# Via environment
export FINFOCUS_LOG_LEVEL=debug
finfocus cost projected --pulumi-json plan.json
# Example debug output:
DBG plugin routing decision resource_type=aws:ec2/instance:Instance provider=aws matched_plugins=[aws-public] selected_plugin=aws-public priority=10 reason=provider
DBG plugin routing decision resource_type=aws:eks:Cluster provider=aws matched_plugins=[eks-costs, aws-public] selected_plugin=eks-costs priority=30 reason=pattern

Check what each plugin supports:

Terminal window
finfocus plugin list --verbose
# Output:
NAME VERSION PROVIDERS CAPABILITIES STATUS
aws-public 1.0.0 aws ProjectedCosts, ActualCosts healthy
aws-ce 1.0.0 aws Recommendations, ActualCosts healthy
gcp-public 1.0.0 gcp ProjectedCosts, ActualCosts healthy

Symptoms: Plugin installed but never queried.

Solutions:

  1. Check plugin is installed:

    Terminal window
    finfocus plugin list
  2. Verify plugin reports correct providers:

    Terminal window
    finfocus plugin list --verbose
  3. Check routing configuration:

    Terminal window
    finfocus config validate
  4. Enable debug logging:

    Terminal window
    finfocus cost projected --debug --pulumi-json plan.json

Symptoms: Primary plugin fails but fallback doesn’t trigger.

Solutions:

  1. Verify fallback: true in config (default):

    - name: primary-plugin
    fallback: true # Must be true
  2. Check plugin is actually failing (not returning $0):

    Terminal window
    finfocus cost projected --debug --pulumi-json plan.json

    Look for ERR or WARN logs indicating failure.

  3. Review fallback trigger reason:

    Terminal window
    finfocus cost projected --debug --pulumi-json plan.json
    # Look for: "plugin failed, trying fallback"

Symptoms: Resource pattern doesn’t route to expected plugin.

Solutions:

  1. Validate pattern syntax:

    Terminal window
    finfocus config validate
  2. For regex, ensure RE2 syntax (no backreferences):

    # CORRECT (RE2 syntax)
    - type: regex
    pattern: 'aws:(ec2|rds)/.*'
    # INCORRECT (backreferences not supported)
    - type: regex
    pattern: "aws:(\\w+)/\\1" # \1 backreference fails
  3. For glob, use * not ** (single-level matching):

    # CORRECT
    - type: glob
    pattern: 'aws:eks:*'
    # INCORRECT (** not supported by filepath.Match)
    - type: glob
    pattern: 'aws:eks:**'
  4. Enable debug logging to see pattern matching:

    Terminal window
    finfocus cost projected --debug --pulumi-json plan.json
    # Look for: "pattern match" events at TRACE level

Symptoms: All plugins fail, falling back to specs or “no cost data”.

Solutions:

  1. Check plugin health:

    Terminal window
    finfocus plugin list --verbose
  2. Verify plugin logs for errors:

    Terminal window
    # Check logs in ~/.finfocus/logs/ if logging enabled
  3. Test individual plugin:

    Terminal window
    finfocus plugin inspect <plugin-name> <resource-type>
  4. Check network connectivity (for cloud API plugins).

Routing decisions are designed to be fast:

  • Provider extraction: ~10ns per resource (simple string split)
  • Glob pattern match: ~100ns per resource (filepath.Match)
  • Regex pattern match: ~200ns per resource (compiled regex cache)
  • Priority sorting: ~O(n log n) where n = matching plugins

Target: <10ms routing overhead per request (SC-002)

Typical: 100-200 microseconds for 10 patterns × 100 resources

Regex patterns are compiled once at config load and cached:

// Compiled at config load
regex, err := regexp.Compile(pattern)
// Cached for subsequent matches
cached[pattern] = regex

Benefit: Avoids recompilation on each resource evaluation.

Before multi-plugin routing, FinFocus queried ALL plugins for ALL resources:

Terminal window
# Old behavior: queries aws-public, gcp-public, recorder for EVERY resource
finfocus cost projected --pulumi-json plan.json

With routing, only matching plugins are queried:

Terminal window
# New behavior: queries only matching plugins
finfocus cost projected --pulumi-json plan.json
# aws:ec2:Instance → queries only aws-public
# gcp:compute:Instance → queries only gcp-public

No configuration: Automatic provider-based routing (backward compatible).

Existing configs: Routing is additive - configs without routing: key work unchanged.

# Legacy config (still works)
plugins:
aws-public:
region: us-east-1
# No routing config = automatic routing based on providers

Plugins reporting ["*"] or empty providers match ALL resources:

# Example: recorder plugin for debugging
routing:
plugins:
- name: recorder
priority: 0 # Query alongside other plugins

Use Cases:

  • Debugging/logging plugins
  • Audit/compliance plugins
  • Testing/validation plugins

When multiple plugins have the same priority, ALL are queried in parallel:

routing:
plugins:
- name: aws-public
priority: 10
- name: aws-ce
priority: 10 # Same priority

Result: Both aws-public and aws-ce queried concurrently, results merged.

Results include which plugin provided the data:

{
"resource": "aws:ec2/instance:Instance",
"monthlyCost": 375.0,
"source": "aws-public", // Attribution field
"currency": "USD"
}

Use Cases:

  • Debugging which plugin responded
  • Comparing results from multiple plugins
  • Audit trails for cost data sources
  1. Start Simple: Begin with automatic routing (zero config), add declarative rules only when needed.

  2. Validate Early: Run finfocus config validate before deploying routing changes.

  3. Use Priorities: Assign clear priority ordering to avoid ambiguity.

  4. Enable Fallback: Keep fallback: true (default) for resilience.

  5. Debug Liberally: Use --debug flag to understand routing decisions.

  6. Pattern Specificity: Make patterns as specific as needed - general patterns can cause unexpected routing.

  7. Monitor Performance: Ensure routing overhead stays <10ms per request.

  • v0.3.1: Documented analyzer/policy-pack mode limitations
    • Routing configuration is not applied when running as a Pulumi policy pack
    • Global plugins (supported_providers: ["*"]) cannot be excluded via routing
    • Added FINFOCUS_HOME isolation procedure in Analyzer Integration docs
  • v0.3.0: Initial multi-plugin routing release
    • Automatic provider-based routing
    • Declarative pattern/feature/priority configuration
    • Validation command and debug logging