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§
Sourcefn 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 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.
Sourcefn 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 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.
Sourcefn 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 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.
Sourcefn 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 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.
Sourcefn 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_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.
Sourcefn 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 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.
Sourcefn 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 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.
Sourcefn 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,
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§
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.