From 854f7bb2bc93b3c33003d3a985a02895a10b48a9 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Mon, 15 Apr 2024 22:01:56 +0200 Subject: [PATCH] refactor data source storage make the associated data stuff work in a type safe way --- Cargo.lock | 239 +++++--------------- terraform-provider-corsschool/Cargo.toml | 4 +- terraform-provider-corsschool/src/client.rs | 3 + terraform-provider-corsschool/src/main.rs | 34 ++- terustform/src/datasource.rs | 29 ++- terustform/src/diag.rs | 7 +- terustform/src/lib.rs | 2 +- terustform/src/provider.rs | 54 ++++- terustform/src/server/grpc.rs | 9 +- terustform/src/server/handler.rs | 165 ++++++++++---- terustform/src/server/mod.rs | 9 +- 11 files changed, 286 insertions(+), 269 deletions(-) create mode 100644 terraform-provider-corsschool/src/client.rs diff --git a/Cargo.lock b/Cargo.lock index bfd7007..aed4add 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -185,22 +185,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - [[package]] name = "deranged" version = "0.3.11" @@ -210,6 +194,15 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "dto" +version = "0.1.0" +source = "git+https://github.com/nilstrieb-lehre/davinci-cors.git?rev=bef75a802cf48cf63d171136c2cea67b83055387#bef75a802cf48cf63d171136c2cea67b83055387" +dependencies = [ + "serde", + "uuid", +] + [[package]] name = "either" version = "1.11.0" @@ -269,21 +262,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.1" @@ -523,6 +501,23 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.3.0", + "hyper-util", + "rustls 0.22.3", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-timeout" version = "0.4.1" @@ -535,22 +530,6 @@ dependencies = [ "tokio-io-timeout", ] -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper 1.3.0", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - [[package]] name = "hyper-util" version = "0.1.3" @@ -715,24 +694,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -783,50 +744,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "openssl" -version = "0.10.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" -dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "overload" version = "0.1.1" @@ -920,12 +837,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - [[package]] name = "powerfmt" version = "0.2.0" @@ -1116,29 +1027,30 @@ dependencies = [ "http-body 1.0.0", "http-body-util", "hyper 1.3.0", - "hyper-tls", + "hyper-rustls", "hyper-util", "ipnet", "js-sys", "log", "mime", - "native-tls", "once_cell", "percent-encoding", "pin-project-lite", + "rustls 0.22.3", "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", - "system-configuration", "tokio", - "tokio-native-tls", + "tokio-rustls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots", "winreg", ] @@ -1255,44 +1167,12 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" -[[package]] -name = "schannel" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "security-framework" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "serde" version = "1.0.197" @@ -1408,27 +1288,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "tempfile" version = "3.10.1" @@ -1445,6 +1304,7 @@ dependencies = [ name = "terraform-provider-corsschool" version = "0.1.0" dependencies = [ + "dto", "eyre", "reqwest", "terustform", @@ -1569,16 +1429,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.25.0" @@ -1792,18 +1642,22 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom", + "serde", +] + [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "want" version = "0.3.1" @@ -1895,6 +1749,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/terraform-provider-corsschool/Cargo.toml b/terraform-provider-corsschool/Cargo.toml index 3245ab4..6aeab26 100644 --- a/terraform-provider-corsschool/Cargo.toml +++ b/terraform-provider-corsschool/Cargo.toml @@ -5,7 +5,9 @@ edition = "2021" [dependencies] eyre = "0.6.12" -reqwest = "0.12.3" +reqwest = { version = "0.12.3", default-features = false, features = ["charset", "http2", "rustls-tls"]} terustform = { path = "../terustform" } tokio = { version = "1.37.0", features = ["full"] } + +dto = { git = "https://github.com/nilstrieb-lehre/davinci-cors.git", rev = "bef75a802cf48cf63d171136c2cea67b83055387" } diff --git a/terraform-provider-corsschool/src/client.rs b/terraform-provider-corsschool/src/client.rs new file mode 100644 index 0000000..635b59a --- /dev/null +++ b/terraform-provider-corsschool/src/client.rs @@ -0,0 +1,3 @@ +pub struct _CorsClient { + client: reqwest::Client +} diff --git a/terraform-provider-corsschool/src/main.rs b/terraform-provider-corsschool/src/main.rs index 720725d..c39e761 100644 --- a/terraform-provider-corsschool/src/main.rs +++ b/terraform-provider-corsschool/src/main.rs @@ -1,25 +1,39 @@ +mod client; + use std::collections::HashMap; use terustform::{ datasource::{self, DataSource}, - provider::Provider, + provider::{MkDataSource, Provider}, AttrPath, DResult, StringValue, Value, ValueModel, }; #[tokio::main] async fn main() -> eyre::Result<()> { - terustform::start(&ExampleProvider {}).await + terustform::start(ExampleProvider {}).await } pub struct ExampleProvider {} impl Provider for ExampleProvider { + type Data = (); fn name(&self) -> String { - "example".to_owned() + "corsschool".to_owned() } - fn data_sources(&self) -> Vec> { - vec![ExampleDataSource {}.erase()] + fn schema(&self) -> datasource::Schema { + datasource::Schema { + description: "uwu".to_owned(), + attributes: HashMap::new(), + } + } + + async fn configure(&self, _config: Value) -> DResult { + Ok(()) + } + + fn data_sources(&self) -> Vec> { + vec![ExampleDataSource::erase()] } } @@ -34,11 +48,13 @@ struct ExampleDataSourceModel { #[terustform::async_trait] impl DataSource for ExampleDataSource { - fn name(&self, provider_name: &str) -> String { + type ProviderData = (); + + fn name(provider_name: &str) -> String { format!("{provider_name}_kitty") } - fn schema(&self) -> datasource::Schema { + fn schema() -> datasource::Schema { datasource::Schema { description: "an example".to_owned(), attributes: HashMap::from([ @@ -70,6 +86,10 @@ impl DataSource for ExampleDataSource { } } + fn new(_data: Self::ProviderData) -> DResult { + Ok(ExampleDataSource {}) + } + async fn read(&self, config: Value) -> DResult { let mut model = ExampleDataSourceModel::from_value(config, &AttrPath::root())?; diff --git a/terustform/src/datasource.rs b/terustform/src/datasource.rs index 438965d..6274b7a 100644 --- a/terustform/src/datasource.rs +++ b/terustform/src/datasource.rs @@ -1,29 +1,44 @@ use std::collections::HashMap; -use crate::values::{Type, Value}; +use crate::{ + provider::{MkDataSource, ProviderData}, + values::{Type, Value}, +}; use super::DResult; #[crate::async_trait] -pub trait DataSource: Send + Sync { - fn name(&self, provider_name: &str) -> String; - fn schema(&self) -> Schema; +pub trait DataSource: 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; - fn erase(self) -> Box + fn name(provider_name: &str) -> String where - Self: Sized + 'static, + Self: Sized; + fn schema() -> Schema + where + Self: Sized; + fn new(data: Self::ProviderData) -> DResult + where + Self: Sized; + + fn erase() -> MkDataSource + where + Self: Sized, { - Box::new(self) + MkDataSource::create::() } } +#[derive(Clone)] pub struct Schema { pub description: String, pub attributes: HashMap, } +#[derive(Clone)] pub enum Attribute { String { description: String, diff --git a/terustform/src/diag.rs b/terustform/src/diag.rs index d4a74aa..116a9d3 100644 --- a/terustform/src/diag.rs +++ b/terustform/src/diag.rs @@ -1,10 +1,10 @@ -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct Diagnostics { pub(crate) diags: Vec, // note: lol this cannot contain warnings that would be fucked oops } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Diagnostic { pub(crate) msg: String, pub(crate) attr: Option, @@ -38,6 +38,9 @@ impl Diagnostic { } impl Diagnostics { + pub fn push(&mut self, d: Diagnostic) { + self.diags.push(d); + } pub fn has_errors(&self) -> bool { !self.diags.is_empty() } diff --git a/terustform/src/lib.rs b/terustform/src/lib.rs index 8e74b8f..72df49b 100644 --- a/terustform/src/lib.rs +++ b/terustform/src/lib.rs @@ -16,7 +16,7 @@ pub use eyre; use provider::Provider; use tracing::Level; -pub async fn start(provider: &dyn Provider) -> eyre::Result<()> { +pub async fn start(provider: P) -> eyre::Result<()> { tracing_subscriber::fmt() .with_max_level(Level::DEBUG) .with_writer(std::io::stderr) diff --git a/terustform/src/provider.rs b/terustform/src/provider.rs index 25d94c0..8a1c6e7 100644 --- a/terustform/src/provider.rs +++ b/terustform/src/provider.rs @@ -1,6 +1,52 @@ -use crate::datasource::DataSource; +use std::{future::Future, sync::Arc}; -pub trait Provider: Send + Sync { - fn name(&self) -> String; - fn data_sources(&self) -> Vec>; +use crate::{ + datasource::{self, DataSource, Schema}, + DResult, Value, +}; + +pub trait ProviderData: Clone + Send + Sync + 'static {} +impl ProviderData for D {} + +pub struct MkDataSource { + pub(crate) name: fn(&str) -> String, + pub(crate) schema: datasource::Schema, + pub(crate) mk: fn(D) -> DResult>, +} + +pub(crate) struct StoredDataSource { + pub(crate) ds: Arc>, + pub(crate) schema: datasource::Schema, +} + +impl Clone for StoredDataSource { + fn clone(&self) -> Self { + Self { + ds: self.ds.clone(), + schema: self.schema.clone(), + } + } +} + +impl MkDataSource { + pub fn create>() -> Self { + Self { + name: Ds::name, + schema: Ds::schema(), + mk: |data| { + Ok(StoredDataSource { + ds: Arc::new(Ds::new(data)?), + schema: Ds::schema(), + }) + }, + } + } +} + +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> + Send; + fn data_sources(&self) -> Vec>; } diff --git a/terustform/src/server/grpc.rs b/terustform/src/server/grpc.rs index b82e60f..6921397 100644 --- a/terustform/src/server/grpc.rs +++ b/terustform/src/server/grpc.rs @@ -36,7 +36,7 @@ fn empty_schema() -> tfplugin6::Schema { } #[tonic::async_trait] -impl Provider for super::ProviderHandler { +impl Provider for super::ProviderHandler

{ /// GetMetadata returns upfront information about server capabilities and /// supported resource types without requiring the server to instantiate all /// schema information, which may be memory intensive. This RPC is optional, @@ -61,7 +61,7 @@ impl Provider for super::ProviderHandler { ) -> Result, Status> { info!("Received get_provider_schema"); - let schemas = self.get_schemas(); + let schemas = self.get_schemas().await; let reply = tfplugin6::get_provider_schema::Response { provider: Some(empty_schema()), @@ -144,9 +144,8 @@ impl Provider for super::ProviderHandler { request: Request, ) -> Result, Status> { tracing::info!("configure_provider"); - let reply = tfplugin6::configure_provider::Response { - diagnostics: vec![], - }; + let diagnostics = self.do_configure_provider(&request.get_ref().config).await; + let reply = tfplugin6::configure_provider::Response { diagnostics }; Ok(Response::new(reply)) } /// ////// Managed Resource Lifecycle diff --git a/terustform/src/server/handler.rs b/terustform/src/server/handler.rs index ccecb6c..fe589e5 100644 --- a/terustform/src/server/handler.rs +++ b/terustform/src/server/handler.rs @@ -1,68 +1,126 @@ use std::collections::HashMap; +use tokio::sync::Mutex; use tokio_util::sync::CancellationToken; -use crate::{datasource::DataSource, provider::Provider}; +use crate::{ + provider::{MkDataSource, Provider, StoredDataSource}, + DResult, Diagnostic, Diagnostics, Type, Value, +}; use super::{grpc::tfplugin6, Schemas}; -pub struct ProviderHandler { +pub struct ProviderHandler { pub(super) shutdown: CancellationToken, /// Delayed diagnostics reporting in `GetProviderSchema` for better UX. - state: Result>, + state: Mutex>, } -struct ProviderState { - data_sources: HashMap>, +enum ProviderState { + Setup { + provider: P, + mk_ds: HashMap>, + }, + Failed { + diags: Diagnostics, + }, + Configured { + data_sources: HashMap>, + }, } -impl ProviderHandler { +impl ProviderHandler

{ /// Creates a new `ProviderHandler`. /// 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: &dyn Provider) -> Self { + 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 data_sources = HashMap::new(); - let mut errors = vec![]; for ds in provider.data_sources() { - let ds_name = ds.name(&name); - let entry = data_sources.insert(ds_name.clone(), ds); + let ds_name = (ds.name)(&name); + let entry = mk_ds.insert(ds_name.clone(), ds); if entry.is_some() { - errors.push(tfplugin6::Diagnostic { - severity: tfplugin6::diagnostic::Severity::Error as _, - summary: format!("data source {ds_name} exists more than once"), - detail: "".to_owned(), - attribute: None, - }); + errors.push(Diagnostic::error_string(format!( + "data source {ds_name} exists more than once" + ))); } } - let state = if errors.len() > 0 { - Err(errors) + let state = if errors.has_errors() { + ProviderState::Failed { diags: errors } } else { - Ok(ProviderState { data_sources }) + ProviderState::Setup { provider, mk_ds } }; - - Self { shutdown, state } + Self { + shutdown, + state: Mutex::new(state), + } } - pub(super) fn get_schemas(&self) -> Schemas { + pub(super) async fn do_configure_provider( + &self, + config: &Option, + ) -> Vec { + let mut state = self.state.lock().await; + let (provider, mk_ds) = match &*state { + ProviderState::Setup { provider, mk_ds } => (provider, mk_ds), + ProviderState::Failed { diags } => return diags.clone().to_tfplugin_diags(), + ProviderState::Configured { .. } => unreachable!("called configure twice"), + }; + let config = match parse_dynamic_value(config, &provider.schema().typ()) { + Ok(config) => config, + Err(errs) => return errs.to_tfplugin_diags(), + }; + + let data = match provider.configure(config).await { + Ok(data) => data, + Err(errs) => return errs.to_tfplugin_diags(), + }; + + let mut data_sources = HashMap::new(); + let mut diags = vec![]; + + for (ds_name, ds) in mk_ds { + let ds = (ds.mk)(data.clone()); + + match ds { + Ok(ds) => { + data_sources.insert(ds_name.clone(), ds); + } + Err(errs) => diags.extend(errs.to_tfplugin_diags()), + } + } + + *state = ProviderState::Configured { data_sources }; + + diags + } + + pub(super) async fn get_schemas(&self) -> Schemas { + let state = self.state.lock().await; let resources = HashMap::new(); - let state = match &self.state { - Ok(state) => state, - Err(errors) => { + let mk_ds = match &*state { + ProviderState::Setup { mk_ds, provider: _ } => mk_ds, + ProviderState::Failed { diags } => { return Schemas { resources: HashMap::new(), data_sources: HashMap::new(), - diagnostics: errors.clone(), + diagnostics: diags.clone().to_tfplugin_diags(), } } + ProviderState::Configured { .. } => { + unreachable!("called get_schemas after configuration") + } }; - let data_sources = state - .data_sources + let data_sources = mk_ds .iter() - .map(|(name, ds)| (name.to_owned(), ds.schema().to_tfplugin())) + .map(|(name, ds)| { + tracing::debug!(?name, "Initializing data source"); + (name.to_owned(), ds.schema.clone().to_tfplugin()) + }) .collect::>(); Schemas { @@ -77,29 +135,31 @@ impl ProviderHandler { type_name: &str, config: &Option, ) -> (Option, Vec) { - let ds = self - .state - .as_ref() - .unwrap() - .data_sources - .get(type_name) - .unwrap(); - - let typ = ds.schema().typ(); - let config = match config { - None => crate::values::Value::Null, - Some(v) => { - let value = crate::values::Value::msg_unpack(&v.msgpack, &typ); - match value { - Ok(value) => value, - Err(errs) => { - return (None, errs.to_tfplugin_diags()); - } + let ds: StoredDataSource = { + let state = self.state.lock().await; + match &*state { + ProviderState::Setup { .. } => { + unreachable!("must be set up before calling data sources") + } + ProviderState::Failed { diags } => { + return (None, diags.clone().to_tfplugin_diags()) + } + ProviderState::Configured { data_sources } => { + data_sources.get(type_name).unwrap().clone() } } }; - let state = ds.read(config).await; + let typ = ds.schema.typ(); + + let config = match parse_dynamic_value(config, &typ) { + Ok(value) => value, + Err(errs) => { + return (None, errs.to_tfplugin_diags()); + } + }; + + let state = ds.ds.read(config).await; let (state, diagnostics) = match state { Ok(s) => ( Some(tfplugin6::DynamicValue { @@ -114,3 +174,10 @@ impl ProviderHandler { (state, diagnostics) } } + +fn parse_dynamic_value(value: &Option, typ: &Type) -> DResult { + match value { + None => Ok(Value::Null), + Some(v) => Value::msg_unpack(&v.msgpack, typ), + } +} diff --git a/terustform/src/server/mod.rs b/terustform/src/server/mod.rs index 83506c3..16d87d9 100644 --- a/terustform/src/server/mod.rs +++ b/terustform/src/server/mod.rs @@ -28,7 +28,7 @@ struct Schemas { diagnostics: Vec, } -pub async fn serve(provider: &dyn Provider) -> eyre::Result<()> { +pub async fn serve(provider: P) -> eyre::Result<()> { let client_cert = std::env::var("PLUGIN_CLIENT_CERT").wrap_err("PLUGIN_CLIENT_CERT not found")?; let client_cert = Certificate::from_pem(client_cert); @@ -58,10 +58,9 @@ pub async fn serve(provider: &dyn Provider) -> eyre::Result<()> { let server = tonic::transport::Server::builder() .tls_config(tls) .wrap_err("invalid TLS config")? - .add_service(ProviderServer::new(handler::ProviderHandler::new( - shutdown.clone(), - provider, - ))) + .add_service(ProviderServer::new( + handler::ProviderHandler::new(shutdown.clone(), provider), + )) .add_service(GrpcControllerServer::new(Controller { shutdown: shutdown.clone(), }))