Skip to main content

Provider

Trait Provider 

Source
pub trait Provider: Send + Sync {
    // Required methods
    fn get_schema<'life0, 'async_trait>(
        &'life0 mut self,
    ) -> Pin<Box<dyn Future<Output = Result<ProviderSchema, ProviderError>> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             Self: 'async_trait;
    fn configure<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 mut self,
        config: &'life1 DynamicValue,
        terraform_version: &'life2 str,
    ) -> Pin<Box<dyn Future<Output = Result<(), ProviderError>> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             Self: 'async_trait;
    fn plan_resource_change<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
        &'life0 mut self,
        type_name: &'life1 str,
        prior_state: &'life2 DynamicValue,
        proposed_new_state: &'life3 DynamicValue,
        config: &'life4 DynamicValue,
    ) -> Pin<Box<dyn Future<Output = Result<PlannedChange, ProviderError>> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             'life3: 'async_trait,
             'life4: 'async_trait,
             Self: 'async_trait;
    fn apply_resource_change<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
        &'life0 mut self,
        type_name: &'life1 str,
        prior_state: &'life2 DynamicValue,
        planned_state: &'life3 DynamicValue,
        config: &'life4 DynamicValue,
    ) -> Pin<Box<dyn Future<Output = Result<DynamicValue, ProviderError>> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             'life3: 'async_trait,
             'life4: 'async_trait,
             Self: 'async_trait;
    fn read_resource<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 mut self,
        type_name: &'life1 str,
        current_state: &'life2 DynamicValue,
    ) -> Pin<Box<dyn Future<Output = Result<Option<DynamicValue>, ProviderError>> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             Self: 'async_trait;
    fn read_data_source<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 mut self,
        type_name: &'life1 str,
        config: &'life2 DynamicValue,
    ) -> Pin<Box<dyn Future<Output = Result<Option<DynamicValue>, ProviderError>> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             Self: 'async_trait;
    fn import_resource_state<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 mut self,
        type_name: &'life1 str,
        id: &'life2 str,
    ) -> Pin<Box<dyn Future<Output = Result<Option<DynamicValue>, ProviderError>> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             Self: 'async_trait;
    fn upgrade_resource_state<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 mut self,
        type_name: &'life1 str,
        stored_version: i64,
        raw_json: &'life2 [u8],
    ) -> Pin<Box<dyn Future<Output = Result<DynamicValue, ProviderError>> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             Self: 'async_trait;
}
Expand description

── THE PROTOCOL DATA MODEL MOVED TO magma-provider-api ──────────── ProviderSchema, PlannedChange, Diag, Severity, ProviderError and is_retryable were defined here. They describe what a provider SAYS, not how it is reached, so they now live in the contract crate — which depends on magma-cty alone, so a native provider can implement the contract without dragging in tonic.

The edge had to point this way: magma-provider-api declares the Provider trait, and THIS crate implements it, so the types could not stay in the crate the trait crate would have had to depend on.

Re-exported unchanged, so every magma_plugin::provider::<T> path still resolves. Relocation, not redesign. A provider: the 8 operations magma performs against one, over magma_cty values.

── THE WHOLE CONTRACT, AND IT IS SMALL ────────────────────────────── This is every call the apply engine makes. There is no ninth. That matters, because the surface being this small is what makes a native provider tractable at all — the cost of a provider is its API bindings, never its protocol.

── Send + Sync, AND Sync IS NOT DECORATION ───────────────────── A LiveProvider is held across .await points inside futures that pangea-operator requires to be Sync, so a Box<dyn Provider> that is merely Send fails to compile at the CONSUMER — six E0277s in another repo, pointing at the operator’s own functions rather than at this line. The concrete ProviderConn this replaced was Sync incidentally, so the bound was being satisfied by accident and erasing it to a trait object is what exposed the requirement.

── &mut self, NOT &self ───────────────────────────────────────── Deliberate, and it constrains the shape downstream. The tonic clients behind ProviderConn need &mut per call, so a &self trait would force interior mutability on the ONE implementation that exists today, to buy sharing that no caller wants: each LiveProvider owns its connection exclusively. Callers therefore hold Box<dyn Provider> (owned), while the FACTORY is what gets shared. A native provider that happens to be stateless can simply ignore the &mut.

── ORDERING IS A REAL PRECONDITION ────────────────────────────────── configure MUST run before any operation other than get_schema. The tfplugin protocol requires it, and providers on both SDKv2 and terraform-plugin-framework cache credentials there — some nil-dereference when called unconfigured. The trait cannot express this (it is a sequencing rule, not a type), so a native implementation must state what it does when called out of order rather than assume the engine’s ordering holds. dial_configured_provider is the one place that establishes it.

Required Methods§

Source

fn get_schema<'life0, 'async_trait>( &'life0 mut self, ) -> Pin<Box<dyn Future<Output = Result<ProviderSchema, ProviderError>> + Send + 'async_trait>>
where 'life0: 'async_trait, Self: 'async_trait,

The provider’s schema, reduced to implied cty types.

Source

fn configure<'life0, 'life1, 'life2, 'async_trait>( &'life0 mut self, config: &'life1 DynamicValue, terraform_version: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<(), ProviderError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, Self: 'async_trait,

Provider credentials / settings. See the ordering note above.

Source

fn plan_resource_change<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 mut self, type_name: &'life1 str, prior_state: &'life2 DynamicValue, proposed_new_state: &'life3 DynamicValue, config: &'life4 DynamicValue, ) -> Pin<Box<dyn Future<Output = Result<PlannedChange, ProviderError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait, Self: 'async_trait,

The provider’s proposed new state PLUS its requires_replace verdict — the only authoritative source for replace-vs-update.

Source

fn apply_resource_change<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 mut self, type_name: &'life1 str, prior_state: &'life2 DynamicValue, planned_state: &'life3 DynamicValue, config: &'life4 DynamicValue, ) -> Pin<Box<dyn Future<Output = Result<DynamicValue, ProviderError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait, Self: 'async_trait,

Execute the change; returns the new state.

A partial apply — the resource is committed but a follow-up call failed — is ProviderError::PartiallyApplied, carrying the state. An implementation that loses that state orphans real resources; see that variant’s doc for the measured EIP case.

Source

fn read_resource<'life0, 'life1, 'life2, 'async_trait>( &'life0 mut self, type_name: &'life1 str, current_state: &'life2 DynamicValue, ) -> Pin<Box<dyn Future<Output = Result<Option<DynamicValue>, ProviderError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, Self: 'async_trait,

Refresh. Ok(None) means the resource no longer exists, so the caller drops it from state — distinct from an error.

Source

fn read_data_source<'life0, 'life1, 'life2, 'async_trait>( &'life0 mut self, type_name: &'life1 str, config: &'life2 DynamicValue, ) -> Pin<Box<dyn Future<Output = Result<Option<DynamicValue>, ProviderError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, Self: 'async_trait,

Read a data source, so ${data.<type>.<name>.<attr>} resolves.

Source

fn import_resource_state<'life0, 'life1, 'life2, 'async_trait>( &'life0 mut self, type_name: &'life1 str, id: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<Option<DynamicValue>, ProviderError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, Self: 'async_trait,

Adopt an existing resource by id — the import half that powers import-on-create-conflict and magma import.

Source

fn upgrade_resource_state<'life0, 'life1, 'life2, 'async_trait>( &'life0 mut self, type_name: &'life1 str, stored_version: i64, raw_json: &'life2 [u8], ) -> Pin<Box<dyn Future<Output = Result<DynamicValue, ProviderError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, Self: 'async_trait,

Migrate stored attribute JSON written under an older schema version up to the current one.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§

Source§

impl Provider for ProviderConn

The gRPC/tfplugin implementation of the provider contract.

Pure delegation to the inherent methods above — this adds no behaviour, it only makes the EXISTING transport one implementation of a contract rather than the only thing an engine can hold.

── ★ WHY EVERY BODY IS FULLY QUALIFIED ────────────────────────────── ProviderConn::get_schema(self), not self.get_schema(). Both resolve to the inherent method — inherent wins over trait — so the short form compiles and works today. But it is one refactor away from disaster: delete or rename the inherent method and self.get_schema() silently rebinds to the TRAIT method, which is this function, and the result is unbounded recursion at runtime rather than an error at compile time. The qualified form cannot rebind: if the inherent method stops existing, this stops compiling.