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 extracts the provider from the resource type’s first segment:

Resource TypeProvider
aws:ec2/instance:Instanceaws
gcp:compute/instance:Instancegcp
azure:compute/virtualMachine:VirtualMachineazure
kubernetes:core/v1:Podkubernetes

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:

TypeSyntaxExampleMatches
glob*, ?, [...]aws:ec2:*aws:ec2:Instance, aws:ec2:SecurityGroup
regexRE2 regexaws: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