mirror of
https://github.com/Noratrieb/terustform.git
synced 2026-01-14 08:30:13 +01:00
Stub out resources
This commit is contained in:
parent
3c891034ee
commit
60de4d5ba8
7 changed files with 155 additions and 39 deletions
2
terraform-provider-corsschool/.gitignore
vendored
2
terraform-provider-corsschool/.gitignore
vendored
|
|
@ -1 +1 @@
|
|||
/.env
|
||||
/.env.sh
|
||||
|
|
|
|||
|
|
@ -4,11 +4,7 @@ mod resources;
|
|||
use std::collections::HashMap;
|
||||
|
||||
use eyre::Context;
|
||||
use terustform::{
|
||||
datasource::DataSource,
|
||||
provider::{MkDataSource, Provider},
|
||||
DResult, EyreExt, Schema, Value,
|
||||
};
|
||||
use terustform::{datasource::DataSource, provider::Provider, DResult, EyreExt, Schema, Value};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> eyre::Result<()> {
|
||||
|
|
@ -44,11 +40,15 @@ impl Provider for ExampleProvider {
|
|||
Ok(client)
|
||||
}
|
||||
|
||||
fn data_sources(&self) -> Vec<MkDataSource<Self::Data>> {
|
||||
fn data_sources(&self) -> terustform::provider::DataSources<Self> {
|
||||
vec![
|
||||
resources::kitty::ExampleDataSource::erase(),
|
||||
resources::hugo::HugoDataSource::erase(),
|
||||
resources::class_data_source::ClassDataSource::erase(),
|
||||
]
|
||||
}
|
||||
|
||||
fn resources(&self) -> terustform::provider::Resources<Self> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,21 +8,6 @@ terraform {
|
|||
|
||||
provider "corsschool" {}
|
||||
|
||||
//resource "terustform_hello" "test1" {}
|
||||
|
||||
data "corsschool_kitty" "kitty" {
|
||||
name = "aa mykitten"
|
||||
}
|
||||
data "corsschool_kitty" "hellyes" {
|
||||
name = "aa a cute kitty"
|
||||
}
|
||||
output "cat1" {
|
||||
value = data.corsschool_kitty.kitty.meow
|
||||
}
|
||||
output "cat2" {
|
||||
value = data.corsschool_kitty.hellyes.meow
|
||||
}
|
||||
|
||||
data "corsschool_hugo" "hugo" {}
|
||||
output "hugo" {
|
||||
value = data.corsschool_hugo.hugo
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ mod values;
|
|||
// Public modules
|
||||
pub mod datasource;
|
||||
pub mod provider;
|
||||
pub mod resource;
|
||||
|
||||
// Re-exports
|
||||
pub use diag::*;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,15 @@
|
|||
use std::{future::Future, sync::Arc};
|
||||
|
||||
use crate::{datasource::DataSource, DResult, Schema, Value};
|
||||
use crate::{datasource::DataSource, resource::Resource, DResult, Schema, Value};
|
||||
|
||||
// This setup is a bit complicated.
|
||||
// In this explanation, substitute "`Resource`" for "`Resource` or `DataSource`".
|
||||
// Semantically, we want to store a `HashMap<String, Box<dyn Resource>>`.
|
||||
// But this doesn't quite work.
|
||||
// The reason for this is that we want our `dyn Resource`s to be able to store `ProviderData` directly.
|
||||
// But `ProviderData` is only available after configuration, and we need to know the schema _before_ configuration.
|
||||
// So we turn the `dyn Resource` into a _statically known_ `MkResource` that contains the constructor and the schema.
|
||||
// Then after configuration, we invoke the constructor and get our `dyn Resource`.
|
||||
|
||||
pub trait ProviderData: Clone + Send + Sync + 'static {}
|
||||
impl<D: Clone + Send + Sync + 'static> ProviderData for D {}
|
||||
|
|
@ -40,10 +49,50 @@ impl<D: ProviderData> MkDataSource<D> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct MkResource<D: ProviderData> {
|
||||
pub(crate) name: fn(&str) -> String,
|
||||
pub(crate) schema: Schema,
|
||||
pub(crate) mk: fn(D) -> DResult<StoredResource<D>>,
|
||||
}
|
||||
|
||||
pub(crate) struct StoredResource<D: ProviderData> {
|
||||
pub(crate) ds: Arc<dyn Resource<ProviderData = D>>,
|
||||
pub(crate) schema: Schema,
|
||||
}
|
||||
|
||||
impl<D: ProviderData> Clone for StoredResource<D> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
ds: self.ds.clone(),
|
||||
schema: self.schema.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: ProviderData> MkResource<D> {
|
||||
pub fn create<Rs: Resource<ProviderData = D>>() -> Self {
|
||||
Self {
|
||||
name: Rs::name,
|
||||
schema: Rs::schema(),
|
||||
mk: |data| {
|
||||
Ok(StoredResource {
|
||||
ds: Arc::new(Rs::new(data)?),
|
||||
schema: Rs::schema(),
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type DataSources<P> = Vec<MkDataSource<<P as Provider>::Data>>;
|
||||
pub type Resources<P> = Vec<MkResource<<P as Provider>::Data>>;
|
||||
|
||||
pub trait Provider: Send + Sync + Sized + 'static {
|
||||
type Data: ProviderData;
|
||||
fn name(&self) -> String;
|
||||
fn schema(&self) -> Schema;
|
||||
fn configure(&self, config: Value) -> impl Future<Output = DResult<Self::Data>> + Send;
|
||||
fn data_sources(&self) -> Vec<MkDataSource<Self::Data>>;
|
||||
fn data_sources(&self) -> DataSources<Self>;
|
||||
fn resources(&self) -> Resources<Self>;
|
||||
}
|
||||
|
|
|
|||
35
terustform/src/resource.rs
Normal file
35
terustform/src/resource.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
use crate::{
|
||||
provider::{MkResource, ProviderData},
|
||||
values::Value,
|
||||
Schema,
|
||||
};
|
||||
|
||||
use super::DResult;
|
||||
|
||||
#[crate::async_trait]
|
||||
pub trait Resource: Send + Sync + 'static {
|
||||
type ProviderData: ProviderData;
|
||||
|
||||
// todo: probably want some kind of Value+Schema thing like tfsdk? whatever.
|
||||
async fn read(&self, config: Value) -> DResult<Value>;
|
||||
async fn create(&self, config: Value) -> DResult<Value>;
|
||||
async fn update(&self, config: Value) -> DResult<Value>;
|
||||
async fn delete(&self, config: Value) -> DResult<Value>;
|
||||
|
||||
fn name(provider_name: &str) -> String
|
||||
where
|
||||
Self: Sized;
|
||||
fn schema() -> Schema
|
||||
where
|
||||
Self: Sized;
|
||||
fn new(data: Self::ProviderData) -> DResult<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn erase() -> MkResource<Self::ProviderData>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
MkResource::create::<Self>()
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ use tokio::sync::Mutex;
|
|||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::{
|
||||
provider::{MkDataSource, Provider, StoredDataSource},
|
||||
provider::{MkDataSource, MkResource, Provider, StoredDataSource, StoredResource},
|
||||
DResult, Diagnostic, Diagnostics, Type, Value,
|
||||
};
|
||||
|
||||
|
|
@ -20,12 +20,14 @@ enum ProviderState<P: Provider> {
|
|||
Setup {
|
||||
provider: P,
|
||||
mk_ds: HashMap<String, MkDataSource<P::Data>>,
|
||||
mk_rs: HashMap<String, MkResource<P::Data>>,
|
||||
},
|
||||
Failed {
|
||||
diags: Diagnostics,
|
||||
},
|
||||
Configured {
|
||||
data_sources: HashMap<String, StoredDataSource<P::Data>>,
|
||||
resources: HashMap<String, StoredResource<P::Data>>,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -34,11 +36,10 @@ impl<P: Provider> ProviderHandler<P> {
|
|||
/// This function is infallible, as it is not called during a time where reporting errors nicely is possible.
|
||||
/// If there's an error, we just taint our internal state and report errors in `GetProviderSchema`.
|
||||
pub fn new(shutdown: CancellationToken, provider: P) -> Self {
|
||||
let mut mk_ds = HashMap::new();
|
||||
|
||||
let mut errors = Diagnostics::default();
|
||||
let name = provider.name();
|
||||
|
||||
let mut mk_ds = HashMap::new();
|
||||
for ds in provider.data_sources() {
|
||||
let ds_name = (ds.name)(&name);
|
||||
let entry = mk_ds.insert(ds_name.clone(), ds);
|
||||
|
|
@ -49,10 +50,25 @@ impl<P: Provider> ProviderHandler<P> {
|
|||
}
|
||||
}
|
||||
|
||||
let mut mk_rs = HashMap::new();
|
||||
for rs in provider.resources() {
|
||||
let rs_name = (rs.name)(&name);
|
||||
let entry = mk_rs.insert(rs_name.clone(), rs);
|
||||
if entry.is_some() {
|
||||
errors.push(Diagnostic::error_string(format!(
|
||||
"data source {rs_name} exists more than once"
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
let state = if errors.has_errors() {
|
||||
ProviderState::Failed { diags: errors }
|
||||
} else {
|
||||
ProviderState::Setup { provider, mk_ds }
|
||||
ProviderState::Setup {
|
||||
provider,
|
||||
mk_ds,
|
||||
mk_rs,
|
||||
}
|
||||
};
|
||||
Self {
|
||||
shutdown,
|
||||
|
|
@ -65,8 +81,12 @@ impl<P: Provider> ProviderHandler<P> {
|
|||
config: &Option<tfplugin6::DynamicValue>,
|
||||
) -> Vec<tfplugin6::Diagnostic> {
|
||||
let mut state = self.state.lock().await;
|
||||
let (provider, mk_ds) = match &*state {
|
||||
ProviderState::Setup { provider, mk_ds } => (provider, mk_ds),
|
||||
let (provider, mk_ds, mk_rs) = match &*state {
|
||||
ProviderState::Setup {
|
||||
provider,
|
||||
mk_ds,
|
||||
mk_rs,
|
||||
} => (provider, mk_ds, mk_rs),
|
||||
ProviderState::Failed { diags } => return diags.clone().to_tfplugin_diags(),
|
||||
ProviderState::Configured { .. } => unreachable!("called configure twice"),
|
||||
};
|
||||
|
|
@ -79,10 +99,9 @@ impl<P: Provider> ProviderHandler<P> {
|
|||
Ok(data) => data,
|
||||
Err(errs) => return errs.to_tfplugin_diags(),
|
||||
};
|
||||
|
||||
let mut data_sources = HashMap::new();
|
||||
let mut diags = vec![];
|
||||
|
||||
let mut data_sources = HashMap::new();
|
||||
for (ds_name, ds) in mk_ds {
|
||||
let ds = (ds.mk)(data.clone());
|
||||
|
||||
|
|
@ -94,16 +113,35 @@ impl<P: Provider> ProviderHandler<P> {
|
|||
}
|
||||
}
|
||||
|
||||
*state = ProviderState::Configured { data_sources };
|
||||
let mut resources = HashMap::new();
|
||||
for (rs_name, rs) in mk_rs {
|
||||
let rs = (rs.mk)(data.clone());
|
||||
|
||||
match rs {
|
||||
Ok(rs) => {
|
||||
resources.insert(rs_name.clone(), rs);
|
||||
}
|
||||
Err(errs) => diags.extend(errs.to_tfplugin_diags()),
|
||||
}
|
||||
}
|
||||
|
||||
*state = ProviderState::Configured {
|
||||
data_sources,
|
||||
resources,
|
||||
};
|
||||
|
||||
diags
|
||||
}
|
||||
|
||||
pub(super) async fn get_schemas(&self) -> Schemas {
|
||||
let state = self.state.lock().await;
|
||||
let resources = HashMap::new();
|
||||
let mk_ds = match &*state {
|
||||
ProviderState::Setup { mk_ds, provider: _ } => mk_ds,
|
||||
|
||||
let (mk_ds, mk_rs) = match &*state {
|
||||
ProviderState::Setup {
|
||||
mk_ds,
|
||||
mk_rs,
|
||||
provider: _,
|
||||
} => (mk_ds, mk_rs),
|
||||
ProviderState::Failed { diags } => {
|
||||
return Schemas {
|
||||
resources: HashMap::new(),
|
||||
|
|
@ -122,6 +160,13 @@ impl<P: Provider> ProviderHandler<P> {
|
|||
(name.to_owned(), ds.schema.clone().to_tfplugin())
|
||||
})
|
||||
.collect::<HashMap<String, tfplugin6::Schema>>();
|
||||
let resources = mk_rs
|
||||
.iter()
|
||||
.map(|(name, ds)| {
|
||||
tracing::debug!(?name, "Initializing resources");
|
||||
(name.to_owned(), ds.schema.clone().to_tfplugin())
|
||||
})
|
||||
.collect::<HashMap<String, tfplugin6::Schema>>();
|
||||
|
||||
Schemas {
|
||||
resources,
|
||||
|
|
@ -144,9 +189,10 @@ impl<P: Provider> ProviderHandler<P> {
|
|||
ProviderState::Failed { diags } => {
|
||||
return (None, diags.clone().to_tfplugin_diags())
|
||||
}
|
||||
ProviderState::Configured { data_sources } => {
|
||||
data_sources.get(type_name).unwrap().clone()
|
||||
}
|
||||
ProviderState::Configured {
|
||||
data_sources,
|
||||
resources: _,
|
||||
} => data_sources.get(type_name).unwrap().clone(),
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue