Hemmer Provider SDK
Rust SDK for building Hemmer providers
The Hemmer Provider SDK provides gRPC protocol types and server helpers for building providers that integrate with Hemmer, the next-generation Infrastructure as Code tool.
Status
🚧 Currently in active development
Features
- Complete Provider Protocol: Full gRPC protocol for infrastructure management
- Schema Support: Define resource and data source schemas with types, validation, and documentation
- Pre-compiled Types: Committed Rust types (no build-time proto generation required)
- Server Helpers: Easy provider startup with handshake protocol
- Validation: Built-in support for provider, resource, and data source config validation
- State Management: Support for state upgrades and resource imports
Installation
Add to your Cargo.toml:
[]
= "0.1"
= { = "1", = ["full"] }
= "1"
Quick Start
use ;
;
async
Automatic Plan Diff Computation
The SDK provides automatic diff computation to simplify plan implementation. Instead of manually constructing AttributeChange instances, use PlanResult::from_diff():
async
The from_diff() method:
- Automatically detects all changes between prior and proposed states
- Supports nested objects with dot-notation paths (e.g.,
"metadata.labels.app") - Supports arrays with bracket notation (e.g.,
"tags[0]") - Returns
no_changewhen states are identical - Marks all fields as added when creating new resources
- Properly handles field additions, removals, and modifications
Manual Plan Construction
For advanced use cases where you need custom logic or want to mark specific changes as requiring replacement, you can still construct plans manually:
use ;
// Manual construction with requires_replace flag
let mut result = from_diff;
// Check if immutable field changed and mark as requiring replacement
if let Some = prior_state.as_ref
Ok
Provider Protocol
The SDK implements a complete provider protocol with the following RPCs:
| RPC | Purpose |
|---|---|
GetMetadata |
Returns provider capabilities and resource/data source names |
GetSchema |
Returns full schema for provider config, resources, and data sources |
ValidateProviderConfig |
Validates provider configuration before use |
Configure |
Configures provider with credentials and settings |
Stop |
Gracefully shuts down the provider |
ValidateResourceConfig |
Validates resource configuration before planning |
UpgradeResourceState |
Migrates state from older schema versions |
Plan |
Calculates required changes to reach desired state |
Create |
Creates a new resource |
Read |
Reads current state of a resource |
Update |
Updates an existing resource |
Delete |
Deletes a resource |
ImportResourceState |
Imports existing infrastructure |
ValidateDataSourceConfig |
Validates data source configuration |
ReadDataSource |
Reads data from external sources |
Schema Types
Define schemas for your resources using the builder pattern:
use *;
let schema = v0
// Required string attribute
.with_attribute
// Optional with default
.with_attribute
// Computed (read-only) attribute
.with_attribute
// Sensitive attribute (hidden in logs)
.with_attribute
// Force replacement when changed
.with_attribute
// Nested blocks
.with_block;
Validation
The SDK provides built-in validation helpers to validate configuration values against schemas:
use ;
use json;
let schema = v0
.with_attribute
.with_attribute;
let value = json!;
// Get detailed validation diagnostics
let diagnostics = validate;
if diagnostics.is_empty
// Or use the simple boolean check
if is_valid
Error Handling
The SDK provides a comprehensive ProviderError enum that maps to appropriate gRPC status codes:
use ProviderError;
// Resource not found (maps to NOT_FOUND)
return Err;
// Resource already exists (maps to ALREADY_EXISTS)
return Err;
// Permission/auth errors (maps to PERMISSION_DENIED)
return Err;
// Rate limiting or quota errors (maps to RESOURCE_EXHAUSTED)
return Err;
// Temporary service issues (maps to UNAVAILABLE)
return Err;
// Timeout errors (maps to DEADLINE_EXCEEDED)
return Err;
// State precondition failures (maps to FAILED_PRECONDITION)
return Err;
// Unimplemented operations (maps to UNIMPLEMENTED)
return Err;
// Validation errors (maps to INVALID_ARGUMENT)
return Err;
// Configuration errors (maps to FAILED_PRECONDITION)
return Err;
Using the appropriate error variant enables:
- Better UX: Users see meaningful error messages
- Retry Logic: Clients can retry on
Unavailablebut notNotFound - Debugging: Error codes help identify root causes quickly
Testing
The SDK includes a test harness for provider implementations:
use ;
use json;
async
Handshake Protocol
When a provider starts via serve(), it outputs a handshake string to stdout:
HEMMER_PROVIDER|1|127.0.0.1:50051
Format: HEMMER_PROVIDER|<protocol_version>|<address>
This allows Hemmer to spawn the provider as a subprocess and connect via gRPC.
Protocol Versioning
The SDK implements protocol version negotiation to ensure compatibility between Hemmer and providers built with different SDK versions.
Version Constants
use ;
// Current protocol version (incremented for breaking changes)
assert_eq!;
// Minimum supported version for backwards compatibility
assert_eq!;
Version Negotiation
During the GetSchema RPC, Hemmer sends its protocol version, and the provider validates compatibility:
// Automatically handled by the SDK
// Providers reject clients with versions below MIN_PROTOCOL_VERSION
check_protocol_version?;
Versioning Strategy
- Patch (0.1.x): Bug fixes, no protocol changes
- Minor (0.x.0): Additive changes (new optional fields), backwards compatible
- Major (x.0.0): Breaking changes, increment
PROTOCOL_VERSION
When PROTOCOL_VERSION is incremented, consider whether older clients should still be supported by setting MIN_PROTOCOL_VERSION appropriately.
Contributing
Quick Setup
# Clone and setup development environment
The setup script will:
- Install git pre-commit hooks
- Verify your Rust toolchain
- Run an initial build and test
Development Workflow
# Build
# Run tests
# Run linter
# Format code
# View documentation
Regenerating Proto Types
Proto types are pre-compiled and committed. To regenerate after changing proto/provider.proto:
# Requires protoc to be installed
Code Style
- Follow standard Rust conventions (rustfmt, clippy)
- Document public APIs with doc comments
- Write tests for new functionality
- Keep commits focused and atomic
Pull Request Process
- Create a feature branch from
main - Make your changes with tests
- Ensure all checks pass (
cargo test,cargo clippy,cargo fmt --check) - Submit a PR using the template
- Address review feedback
Related Projects
- hemmer - The Hemmer IaC tool
- hemmer-provider-generator - Generate providers from OpenAPI specs
License
Licensed under the Apache License, Version 2.0. See LICENSE for details.