| Crates.io | lmrc-ports |
| lib.rs | lmrc-ports |
| version | 0.3.16 |
| created_at | 2025-11-28 21:05:57.543963+00 |
| updated_at | 2025-12-11 13:25:31.138997+00 |
| description | Port trait definitions for LMRC Stack hexagonal architecture |
| homepage | |
| repository | https://gitlab.com/lemarco/lmrc-stack |
| max_upload_size | |
| id | 1956036 |
| size | 51,045 |
Port trait definitions for LMRC Stack hexagonal architecture.
This library defines the core abstractions (ports) that separate business logic from infrastructure implementation details. Provider-specific implementations (adapters) implement these traits to enable easy swapping of cloud providers, DNS services, and other infrastructure components.
┌─────────────────────────────────────────┐
│ Business Logic (Pipeline Steps) │
└─────────────────┬───────────────────────┘
│ depends on
▼
┌─────────────────────────────────────────┐
│ Ports (This Library) │
│ - ServerProvider trait │
│ - DnsProvider trait │
│ - K8sInstaller trait │
│ - DatabaseProvider trait │
│ - GitProvider trait │
└─────────────────▲───────────────────────┘
│ implements
│
┌─────────────────┴───────────────────────┐
│ Adapters (Provider-Specific) │
│ - lmrc-hetzner-adapter │
│ - lmrc-aws-adapter (planned) │
│ - lmrc-cloudflare-adapter (planned) │
│ - etc. │
└─────────────────────────────────────────┘
Abstraction for server provisioning operations across cloud providers (Hetzner, AWS, GCP, etc.).
use lmrc_ports::{ServerProvider, ProvisionRequest, ProvisionedServer};
async fn provision_server(provider: &dyn ServerProvider) -> Result<ProvisionedServer> {
let request = ProvisionRequest {
name: "web-server".to_string(),
server_type: "cx11".to_string(),
image: "ubuntu-22.04".to_string(),
location: "nbg1".to_string(),
// ...
};
provider.provision_server(request).await
}
Methods:
provision_server() - Create and start a new serverlist_servers() - List all serversget_server() - Get server by IDget_server_by_name() - Find server by namedelete_server() - Permanently delete a serverpower_on() - Start a stopped serverpower_off() - Stop a running serverreboot() - Restart a serverAbstraction for DNS management across providers (Cloudflare, Route53, etc.).
use lmrc_ports::{DnsProvider, DnsRecordRequest, DnsRecordType};
async fn create_dns_record(provider: &dyn DnsProvider, zone_id: &str) {
let request = DnsRecordRequest {
record_type: DnsRecordType::A,
name: "www".to_string(),
content: "192.0.2.1".to_string(),
ttl: Some(3600),
proxied: false,
priority: None,
};
provider.create_record(zone_id, request).await?;
}
Methods:
create_record() - Create a new DNS recordlist_records() - List all DNS records in a zoneget_record() - Get a specific DNS recordupdate_record() - Update an existing DNS recorddelete_record() - Delete a DNS recordfind_record_by_name() - Find record by nameAbstraction for Kubernetes cluster installation (K3s, EKS, AKS, GKE, etc.).
use lmrc_ports::{K8sInstaller, ClusterInstallRequest};
async fn install_cluster(installer: &dyn K8sInstaller) {
let request = ClusterInstallRequest {
name: "production".to_string(),
version: Some("1.28".to_string()),
master_nodes: vec!["10.0.1.10".to_string()],
worker_nodes: vec!["10.0.1.11".to_string(), "10.0.1.12".to_string()],
options: HashMap::new(),
};
let cluster = installer.install_cluster(request).await?;
println!("Kubeconfig: {}", cluster.kubeconfig);
}
Methods:
install_cluster() - Install and configure a new clusteruninstall_cluster() - Remove a clusterget_cluster_status() - Get cluster health statusget_kubeconfig() - Retrieve kubeconfig contentAbstraction for database management (PostgreSQL, MySQL, RDS, etc.).
use lmrc_ports::{DatabaseProvider, DatabaseCreateRequest};
async fn create_database(provider: &dyn DatabaseProvider) {
let request = DatabaseCreateRequest {
name: "myapp_production".to_string(),
owner: "myapp_user".to_string(),
encoding: Some("UTF8".to_string()),
locale: Some("en_US.UTF-8".to_string()),
};
let db = provider.create_database(request).await?;
println!("Connection string: {}", db.connection_string);
}
Methods:
create_database() - Create a new databasedrop_database() - Delete a databaselist_databases() - List all databasesdatabase_exists() - Check if database existscreate_user() - Create a database userdrop_user() - Delete a database usergrant_privileges() - Grant user permissionsAbstraction for Git platform operations (GitLab, GitHub, etc.).
use lmrc_ports::{GitProvider, CiVariableRequest};
async fn setup_ci_variables(provider: &dyn GitProvider, project_id: &str) {
let variable = CiVariableRequest {
key: "DATABASE_URL".to_string(),
value: "postgres://...".to_string(),
protected: true,
masked: true,
};
provider.create_ci_variable(project_id, variable).await?;
}
Methods:
get_repository() - Get repository informationcreate_ci_variable() - Create a CI/CD variableupdate_ci_variable() - Update a CI/CD variablelist_ci_variables() - List all CI/CD variablesdelete_ci_variable() - Delete a CI/CD variabletrigger_pipeline() - Trigger a new pipeline runget_pipeline() - Get pipeline statusProvider-agnostic domain models that work across all implementations:
ProvisionRequest / ProvisionedServerDnsRecordRequest / DnsRecordClusterInstallRequest / InstalledClusterDatabaseCreateRequest / CreatedDatabaseCiVariableRequest / CiVariableAll ports use a unified error type:
pub enum PortError {
NotFound { resource_type: String, resource_id: String },
AlreadyExists { resource_type: String, resource_id: String },
AuthenticationFailed(String),
InvalidConfiguration(String),
NetworkError(String),
Timeout { operation: String, seconds: u64 },
ProviderError(String),
OperationFailed(String),
InvalidState(String),
RateLimitExceeded { retry_after_seconds: Option<u64> },
}
To add support for a new provider:
Create a new adapter crate:
cargo new --lib libs/lmrc-mycloud-adapter
Add dependencies:
[dependencies]
lmrc-ports = { workspace = true }
async-trait = { workspace = true }
# Your cloud provider SDK
Implement the port trait:
use async_trait::async_trait;
use lmrc_ports::{ServerProvider, ProvisionRequest, ProvisionedServer, PortResult};
pub struct MyCloudAdapter {
client: MyCloudClient,
}
#[async_trait]
impl ServerProvider for MyCloudAdapter {
async fn provision_server(&self, request: ProvisionRequest)
-> PortResult<ProvisionedServer> {
// Convert request to provider-specific format
// Call provider API
// Convert response back to ProvisionedServer
todo!()
}
// Implement other methods...
}
Add to provider factory:
Update lmrc-pipeline/src/factory.rs to include your adapter.
Implements ServerProvider for Hetzner Cloud:
use lmrc_hetzner_adapter::HetznerAdapter;
let adapter = HetznerAdapter::from_env()?;
let servers = adapter.list_servers().await?;
Ports make it easy to test business logic with mock implementations:
use async_trait::async_trait;
use lmrc_ports::{ServerProvider, ProvisionRequest, ProvisionedServer, PortResult};
struct MockServerProvider {
servers: Vec<ProvisionedServer>,
}
#[async_trait]
impl ServerProvider for MockServerProvider {
async fn provision_server(&self, request: ProvisionRequest)
-> PortResult<ProvisionedServer> {
Ok(ProvisionedServer {
id: "mock-123".to_string(),
name: request.name,
// ...
})
}
// Mock other methods...
}
// Use in tests
#[tokio::test]
async fn test_provisioning_logic() {
let mock_provider = MockServerProvider { servers: vec![] };
// Test your business logic with the mock
}
MIT OR Apache-2.0