recorder

package
v0.3.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 31, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

README

Recorder Plugin

A reference implementation plugin that records all gRPC requests to JSON files and optionally returns mock cost responses. This plugin serves as both a developer tool for inspecting Core-to-plugin data shapes and a canonical reference implementation demonstrating pluginsdk v0.4.6 patterns.

Features

  • Request Recording: Captures all gRPC requests to JSON files for inspection
  • Mock Responses: Optionally returns randomized but valid cost responses
  • Reference Implementation: Demonstrates pluginsdk v0.4.6 best practices
  • Thread-Safe: Safe for concurrent use with sync.Mutex protection
  • Graceful Shutdown: Proper signal handling and cleanup

Installation

# From finfocus-core repository root
make install-recorder

# Verify installation
./bin/finfocus plugin list

This builds the plugin and installs it to ~/.finfocus/plugins/recorder/0.1.0/.

Configuration

Configuration is via environment variables:

Variable Default Description
FINFOCUS_RECORDER_OUTPUT_DIR ./recorded_data Directory for recorded JSON files
FINFOCUS_RECORDER_MOCK_RESPONSE false Enable randomized mock responses

Usage

Basic Recording (Default Mode)
# Set output directory (optional)
export FINFOCUS_RECORDER_OUTPUT_DIR=./my-recordings

# Run cost calculation - requests will be recorded
./bin/finfocus cost projected --pulumi-json plan.json

# View recorded files
ls -la ./my-recordings/
cat ./my-recordings/*.json | jq .
Mock Response Mode
# Enable mock responses
export FINFOCUS_RECORDER_MOCK_RESPONSE=true

# Run cost calculation - will return randomized costs
./bin/finfocus cost projected --pulumi-json plan.json --output json

Recorded Request Format

Each request creates a JSON file with the following structure:

{
  "timestamp": "2025-12-11T14:30:52Z",
  "method": "GetProjectedCost",
  "requestId": "01JEK7X2J3K4M5N6P7Q8R9S0T1",
  "request": {
    "resource": {
      "resourceType": "aws:ec2:Instance",
      "provider": "aws",
      "sku": "t3.medium",
      "region": "us-east-1"
    }
  },
  "metadata": {
    "receivedAt": "2025-12-11T14:30:52.123456Z",
    "processingTimeMs": 2
  }
}
File Naming

Files are named with the format: <timestamp>_<method>_<ulid>.json

Example: 20251211T143052Z_GetProjectedCost_01JEK7X2J3K4M5N6P7Q8R9S0T1.json

Use Cases

1. Debug Plugin Integration

See exactly what data Core sends to plugins:

FINFOCUS_RECORDER_OUTPUT_DIR=./debug ./bin/finfocus cost projected --pulumi-json my-plan.json
cat ./debug/*.json | jq '.request.resource'
2. Test Core Aggregation

Generate mock data to test output formatting:

FINFOCUS_RECORDER_MOCK_RESPONSE=true ./bin/finfocus cost projected \
  --pulumi-json plan.json \
  --output table
3. Contract Testing

Use as a reference plugin in integration tests:

# In test setup
export FINFOCUS_RECORDER_OUTPUT_DIR=/tmp/test-recordings
export FINFOCUS_RECORDER_MOCK_RESPONSE=true

# Run tests
go test ./test/integration/... -v

SDK Patterns Demonstrated

This plugin demonstrates the following pluginsdk v0.4.6 patterns:

  1. BasePlugin Embedding: Using *pluginsdk.BasePlugin for common functionality
  2. Request Validation: Using pluginsdk.ValidateProjectedCostRequest() and similar helpers
  3. Graceful Shutdown: Context-based cancellation with signal handling
  4. Structured Logging: Using zerolog for consistent log output
  5. Thread Safety: Using sync.Mutex for concurrent request handling

Development

Running Tests
# Run all tests
go test -v ./plugins/recorder/...

# Run with coverage
go test -cover ./plugins/recorder/...

# Run benchmarks
go test -bench=. ./plugins/recorder/...
Code Structure
plugins/recorder/
├── cmd/
│   └── main.go           # Plugin entry point
├── config.go             # Configuration loading
├── config_test.go        # Config tests
├── mocker.go             # Mock response generation
├── mocker_test.go        # Mocker tests
├── plugin.go             # Main plugin implementation
├── plugin_test.go        # Plugin tests
├── recorder.go           # Request recording
├── recorder_test.go      # Recorder tests
├── plugin.manifest.json  # Plugin metadata
└── README.md             # This file

Troubleshooting

Plugin Not Found
# Check plugin directory structure
ls -la ~/.finfocus/plugins/recorder/

# Verify binary is executable
chmod +x ~/.finfocus/plugins/recorder/0.1.0/finfocus-plugin-recorder
No Files Being Recorded
# Check output directory exists and is writable
mkdir -p "$FINFOCUS_RECORDER_OUTPUT_DIR"
touch "$FINFOCUS_RECORDER_OUTPUT_DIR/test.txt" && rm "$FINFOCUS_RECORDER_OUTPUT_DIR/test.txt"

# Check plugin is being used
./bin/finfocus cost projected --debug --pulumi-json plan.json 2>&1 | grep recorder
Mock Responses Not Working
# Verify environment variable is set correctly
echo $FINFOCUS_RECORDER_MOCK_RESPONSE  # Should be "true"

# Case-insensitive values work: true, TRUE, 1, yes, on
export FINFOCUS_RECORDER_MOCK_RESPONSE=TRUE

License

Part of FinFocus Core. See repository license for details.

Documentation

Overview

Package recorder implements a reference plugin that records all gRPC requests and optionally returns mock cost responses.

Index

Constants

View Source
const (
	EnvOutputDir    = "FINFOCUS_RECORDER_OUTPUT_DIR"
	EnvMockResponse = "FINFOCUS_RECORDER_MOCK_RESPONSE"
)

Environment variable names for configuration.

View Source
const (
	DefaultOutputDir    = "./recorded_data"
	DefaultMockResponse = false
)

Default configuration values.

View Source
const (
	// MinProjectedCost is the minimum projected cost: $0.01 per month.
	MinProjectedCost = 0.01
	// MaxProjectedCost is the maximum projected cost: $1000 per month.
	MaxProjectedCost = 1000.0

	// MinActualCost is the minimum actual cost: $0.001 per day.
	MinActualCost = 0.001
	// MaxActualCost is the maximum actual cost: $100 per day.
	MaxActualCost = 100.0

	// HoursPerMonth is the standard hours per month for cost conversions.
	HoursPerMonth = 730.0
)

Cost range constants for mock generation.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// OutputDir is the directory where recorded JSON files are written.
	// Default: "./recorded_data"
	OutputDir string

	// MockResponse enables randomized mock cost responses.
	// When false (default), the plugin returns zero/empty costs.
	MockResponse bool
}

Config holds runtime configuration for the recorder plugin. Configuration is loaded from environment variables with sensible defaults.

func LoadConfig

func LoadConfig() *Config

LoadConfig creates a Config from environment variables. Missing variables use default values.

type Mocker

type Mocker struct {
	// contains filtered or unexported fields
}

Mocker generates randomized but valid cost responses for testing.

func NewMocker

func NewMocker(logger zerolog.Logger) *Mocker

NewMocker creates a new Mocker instance.

func (*Mocker) CreateActualCostResponse

func (m *Mocker) CreateActualCostResponse() *pbc.GetActualCostResponse

CreateActualCostResponse creates a mock GetActualCostResponse.

func (*Mocker) CreateEstimateCostResponse

func (m *Mocker) CreateEstimateCostResponse() *pbc.EstimateCostResponse

CreateEstimateCostResponse creates a mock EstimateCostResponse.

func (*Mocker) CreateProjectedCostResponse

func (m *Mocker) CreateProjectedCostResponse() *pbc.GetProjectedCostResponse

CreateProjectedCostResponse creates a mock GetProjectedCostResponse.

func (*Mocker) CreateRecommendationsResponse added in v0.2.4

func (m *Mocker) CreateRecommendationsResponse() *pbc.GetRecommendationsResponse

CreateRecommendationsResponse creates a mock GetRecommendationsResponse.

func (*Mocker) GenerateActualCost

func (m *Mocker) GenerateActualCost() float64

GenerateActualCost generates a randomized daily cost. Uses log-scale distribution for realistic cost spread.

func (*Mocker) GenerateProjectedCost

func (m *Mocker) GenerateProjectedCost() float64

GenerateProjectedCost generates a randomized monthly cost. Uses log-scale distribution for realistic cost spread.

func (*Mocker) GenerateRecommendations added in v0.2.4

func (m *Mocker) GenerateRecommendations() []*pbc.Recommendation

GenerateRecommendations generates a randomized list of recommendations.

type RecordedRequest

type RecordedRequest struct {
	Timestamp string          `json:"timestamp"`
	Method    string          `json:"method"`
	RequestID string          `json:"requestId"`
	Request   json.RawMessage `json:"request"`
	Metadata  RequestMetadata `json:"metadata"`
}

RecordedRequest represents a captured gRPC request.

type Recorder

type Recorder struct {
	// contains filtered or unexported fields
}

Recorder handles serialization of gRPC requests to JSON files. It is thread-safe and handles I/O errors gracefully.

func NewRecorder

func NewRecorder(outputDir string, logger zerolog.Logger) *Recorder

NewRecorder creates a new Recorder instance. It creates the output directory if it doesn't exist.

func (*Recorder) Close

func (r *Recorder) Close()

Close performs cleanup when the recorder is shutting down.

func (*Recorder) RecordRequest

func (r *Recorder) RecordRequest(method string, req proto.Message) error

RecordRequest serializes a gRPC request to a JSON file. It returns an error if the write fails, but the caller should typically log and continue rather than failing the request.

type RecorderPlugin

type RecorderPlugin struct {
	*pluginsdk.BasePlugin
	// contains filtered or unexported fields
}

RecorderPlugin implements the CostSourceService interface. It records all incoming gRPC requests to JSON files and optionally returns mock cost responses for testing purposes.

This plugin serves as a reference implementation demonstrating pluginsdk v0.4.6 patterns including:

  • BasePlugin embedding for common functionality
  • Request validation using SDK helpers
  • Graceful shutdown handling
  • Thread-safe operation

func NewRecorderPlugin

func NewRecorderPlugin(cfg *Config, logger zerolog.Logger) *RecorderPlugin

NewRecorderPlugin creates a new recorder plugin instance. The plugin is configured via the provided Config and will:

  • Record all requests to JSON files in Config.OutputDir
  • Return mock responses if Config.MockResponse is true
  • Return zero/empty costs if Config.MockResponse is false

func (*RecorderPlugin) EstimateCost

EstimateCost returns a cost estimate for a resource. When mock responses are enabled, it generates randomized cost data; otherwise it returns a zero-cost response.

func (*RecorderPlugin) GetActualCost

GetActualCost handles actual cost requests. It validates the request, records it to disk, and returns either:

  • Mock cost data (if MockResponse is enabled)
  • Empty results (if MockResponse is disabled)

func (*RecorderPlugin) GetPluginInfo added in v0.2.4

GetPluginInfo returns information about the plugin.

func (*RecorderPlugin) GetPricingSpec

GetPricingSpec returns the pricing specification for a resource. The recorder plugin returns an empty response since it does not provide real pricing data.

func (*RecorderPlugin) GetProjectedCost

GetProjectedCost handles projected cost requests. It validates the request, records it to disk, and returns either:

  • Mock cost data (if MockResponse is enabled)
  • Zero cost with explanatory note (if MockResponse is disabled)

func (*RecorderPlugin) GetRecommendations added in v0.2.4

GetRecommendations handles recommendations requests.

func (*RecorderPlugin) Name

func (p *RecorderPlugin) Name() string

Name returns the plugin identifier. This method satisfies the pluginsdk.Plugin interface.

func (*RecorderPlugin) Shutdown

func (p *RecorderPlugin) Shutdown()

Shutdown performs graceful shutdown of the plugin. It flushes any pending writes and releases resources.

func (*RecorderPlugin) Supports added in v0.3.0

Supports overrides BasePlugin's default to always return false. The recorder is a synthetic/demo plugin and should not be auto-selected for real cost processing by the engine's resource matching logic. The request is still recorded to disk for debugging purposes.

type RequestMetadata

type RequestMetadata struct {
	ReceivedAt       string `json:"receivedAt"`
	ProcessingTimeMs int64  `json:"processingTimeMs"`
}

RequestMetadata contains optional metadata about the request.

Directories

Path Synopsis
Package main provides the entry point for the recorder plugin.
Package main provides the entry point for the recorder plugin.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL