mirror of
https://github.com/Noratrieb/terustform.git
synced 2026-01-14 08:30:13 +01:00
refactor data source storage
make the associated data stuff work in a type safe way
This commit is contained in:
parent
b5a28e4e94
commit
854f7bb2bc
11 changed files with 286 additions and 269 deletions
239
Cargo.lock
generated
239
Cargo.lock
generated
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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" }
|
||||
|
|
|
|||
3
terraform-provider-corsschool/src/client.rs
Normal file
3
terraform-provider-corsschool/src/client.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub struct _CorsClient {
|
||||
client: reqwest::Client
|
||||
}
|
||||
|
|
@ -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<Box<dyn DataSource>> {
|
||||
vec![ExampleDataSource {}.erase()]
|
||||
fn schema(&self) -> datasource::Schema {
|
||||
datasource::Schema {
|
||||
description: "uwu".to_owned(),
|
||||
attributes: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn configure(&self, _config: Value) -> DResult<Self::Data> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn data_sources(&self) -> Vec<MkDataSource<Self::Data>> {
|
||||
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<Self> {
|
||||
Ok(ExampleDataSource {})
|
||||
}
|
||||
|
||||
async fn read(&self, config: Value) -> DResult<Value> {
|
||||
let mut model = ExampleDataSourceModel::from_value(config, &AttrPath::root())?;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Value>;
|
||||
|
||||
fn erase(self) -> Box<dyn DataSource>
|
||||
fn name(provider_name: &str) -> String
|
||||
where
|
||||
Self: Sized + 'static,
|
||||
Self: Sized;
|
||||
fn schema() -> Schema
|
||||
where
|
||||
Self: Sized;
|
||||
fn new(data: Self::ProviderData) -> DResult<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn erase() -> MkDataSource<Self::ProviderData>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Box::new(self)
|
||||
MkDataSource::create::<Self>()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Schema {
|
||||
pub description: String,
|
||||
pub attributes: HashMap<String, Attribute>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Attribute {
|
||||
String {
|
||||
description: String,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Diagnostics {
|
||||
pub(crate) diags: Vec<Diagnostic>,
|
||||
// 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<AttrPath>,
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<P: Provider>(provider: P) -> eyre::Result<()> {
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(Level::DEBUG)
|
||||
.with_writer(std::io::stderr)
|
||||
|
|
|
|||
|
|
@ -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<Box<dyn DataSource>>;
|
||||
use crate::{
|
||||
datasource::{self, DataSource, Schema},
|
||||
DResult, Value,
|
||||
};
|
||||
|
||||
pub trait ProviderData: Clone + Send + Sync + 'static {}
|
||||
impl<D: Clone + Send + Sync + 'static> ProviderData for D {}
|
||||
|
||||
pub struct MkDataSource<D: ProviderData> {
|
||||
pub(crate) name: fn(&str) -> String,
|
||||
pub(crate) schema: datasource::Schema,
|
||||
pub(crate) mk: fn(D) -> DResult<StoredDataSource<D>>,
|
||||
}
|
||||
|
||||
pub(crate) struct StoredDataSource<D: ProviderData> {
|
||||
pub(crate) ds: Arc<dyn DataSource<ProviderData = D>>,
|
||||
pub(crate) schema: datasource::Schema,
|
||||
}
|
||||
|
||||
impl<D: ProviderData> Clone for StoredDataSource<D> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
ds: self.ds.clone(),
|
||||
schema: self.schema.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: ProviderData> MkDataSource<D> {
|
||||
pub fn create<Ds: DataSource<ProviderData = D>>() -> 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<Output = DResult<Self::Data>> + Send;
|
||||
fn data_sources(&self) -> Vec<MkDataSource<Self::Data>>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ fn empty_schema() -> tfplugin6::Schema {
|
|||
}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl Provider for super::ProviderHandler {
|
||||
impl<P: crate::provider::Provider> Provider for super::ProviderHandler<P> {
|
||||
/// 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<Response<tfplugin6::get_provider_schema::Response>, 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<tfplugin6::configure_provider::Request>,
|
||||
) -> Result<Response<tfplugin6::configure_provider::Response>, 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
|
||||
|
|
|
|||
|
|
@ -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<P: Provider> {
|
||||
pub(super) shutdown: CancellationToken,
|
||||
/// Delayed diagnostics reporting in `GetProviderSchema` for better UX.
|
||||
state: Result<ProviderState, Vec<tfplugin6::Diagnostic>>,
|
||||
state: Mutex<ProviderState<P>>,
|
||||
}
|
||||
|
||||
struct ProviderState {
|
||||
data_sources: HashMap<String, Box<dyn DataSource>>,
|
||||
enum ProviderState<P: Provider> {
|
||||
Setup {
|
||||
provider: P,
|
||||
mk_ds: HashMap<String, MkDataSource<P::Data>>,
|
||||
},
|
||||
Failed {
|
||||
diags: Diagnostics,
|
||||
},
|
||||
Configured {
|
||||
data_sources: HashMap<String, StoredDataSource<P::Data>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl ProviderHandler {
|
||||
impl<P: Provider> ProviderHandler<P> {
|
||||
/// 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<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),
|
||||
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::<HashMap<String, tfplugin6::Schema>>();
|
||||
|
||||
Schemas {
|
||||
|
|
@ -77,29 +135,31 @@ impl ProviderHandler {
|
|||
type_name: &str,
|
||||
config: &Option<tfplugin6::DynamicValue>,
|
||||
) -> (Option<tfplugin6::DynamicValue>, Vec<tfplugin6::Diagnostic>) {
|
||||
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<P::Data> = {
|
||||
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<tfplugin6::DynamicValue>, typ: &Type) -> DResult<Value> {
|
||||
match value {
|
||||
None => Ok(Value::Null),
|
||||
Some(v) => Value::msg_unpack(&v.msgpack, typ),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ struct Schemas {
|
|||
diagnostics: Vec<tfplugin6::Diagnostic>,
|
||||
}
|
||||
|
||||
pub async fn serve(provider: &dyn Provider) -> eyre::Result<()> {
|
||||
pub async fn serve<P: Provider>(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(),
|
||||
}))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue