restructure modules

This commit is contained in:
nora 2024-04-15 20:22:45 +02:00
parent b86292fadb
commit 7e479e7a28
10 changed files with 114 additions and 114 deletions

View file

@ -1,17 +1,15 @@
use std::collections::HashMap; use std::collections::HashMap;
use terustform::{ use terustform::{
framework::{ datasource::{self, DataSource},
datasource::{self, DataSource}, provider::Provider,
provider::Provider,
AttrPath, DResult, StringValue, ValueModel,
},
values::Value, values::Value,
AttrPath, DResult, StringValue, ValueModel,
}; };
#[tokio::main] #[tokio::main]
async fn main() -> eyre::Result<()> { async fn main() -> eyre::Result<()> {
terustform::serve(&ExampleProvider {}).await terustform::start(&ExampleProvider {}).await
} }
pub struct ExampleProvider {} pub struct ExampleProvider {}
@ -28,7 +26,7 @@ impl Provider for ExampleProvider {
struct ExampleDataSource {} struct ExampleDataSource {}
#[derive(terustform::DataSourceModel)] #[derive(terustform::Model)]
struct ExampleDataSourceModel { struct ExampleDataSourceModel {
name: StringValue, name: StringValue,
meow: StringValue, meow: StringValue,

View file

@ -3,7 +3,7 @@ use syn::spanned::Spanned;
// This macro should only reference items in `terustform::__derive_private`. // This macro should only reference items in `terustform::__derive_private`.
#[proc_macro_derive(DataSourceModel)] #[proc_macro_derive(Model)]
pub fn data_source_model(input: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn data_source_model(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput); let input = syn::parse_macro_input!(input as syn::DeriveInput);
match data_source_model_inner(input) { match data_source_model_inner(input) {

View file

@ -1,12 +1,5 @@
#![allow(dead_code)]
pub mod datasource;
pub mod provider;
use crate::values::{Value, ValueKind}; use crate::values::{Value, ValueKind};
use self::datasource::DataSource;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Diagnostics { pub struct Diagnostics {
pub(crate) diags: Vec<Diagnostic>, pub(crate) diags: Vec<Diagnostic>,

View file

@ -1,113 +1,33 @@
mod cert; mod base;
pub mod framework; pub mod datasource;
pub mod provider;
mod server; mod server;
pub mod values; pub mod values;
pub use terustform_macros::DataSourceModel; pub use base::*;
pub use terustform_macros::Model;
use std::{env, path::PathBuf}; use provider::Provider;
use base64::Engine; use tracing::Level;
use eyre::{bail, Context, Result};
use framework::provider::Provider;
use tokio::net::UnixListener;
use tonic::transport::{Certificate, ServerTlsConfig};
use tracing::{info, Level};
pub async fn serve(provider: &dyn Provider) -> eyre::Result<()> { pub async fn start(provider: &dyn Provider) -> eyre::Result<()> {
tracing_subscriber::fmt() tracing_subscriber::fmt()
.with_max_level(Level::DEBUG) .with_max_level(Level::DEBUG)
.with_writer(std::io::stderr) .with_writer(std::io::stderr)
.without_time() .without_time()
.init(); .init();
let client_cert = server::serve(provider).await
std::env::var("PLUGIN_CLIENT_CERT").wrap_err("PLUGIN_CLIENT_CERT not found")?;
let client_cert = Certificate::from_pem(client_cert);
let (server_identity, server_cert) =
cert::generate_cert().wrap_err("generating server certificate")?;
let (_tmpdir, socket) = match init_handshake(&server_cert).await {
Ok(addr) => addr,
Err(err) => {
println!("{:?}", err);
bail!("init error");
}
};
let tls = ServerTlsConfig::new()
.identity(server_identity)
.client_auth_optional(true) // ??? terraform doesn't send certs ???
.client_ca_root(client_cert);
info!("Listening on {}", socket.display());
let uds = UnixListener::bind(socket).wrap_err("failed to bind unix listener")?;
let uds_stream = tokio_stream::wrappers::UnixListenerStream::new(uds);
let shutdown = tokio_util::sync::CancellationToken::new();
let server = tonic::transport::Server::builder()
.tls_config(tls)
.wrap_err("invalid TLS config")?
.add_service(server::ProviderServer::new(server::ProviderHandler::new(
shutdown.clone(),
provider,
)))
.add_service(server::GrpcControllerServer::new(server::Controller {
shutdown: shutdown.clone(),
}))
.serve_with_incoming(uds_stream);
tokio::select! {
_ = shutdown.cancelled() => {}
result = server => {
result.wrap_err("failed to start server")?;
}
}
Ok(())
}
const _MAGIC_COOKIE_KEY: &str = "TF_PLUGIN_MAGIC_COOKIE";
const _MAGIC_COOKIE_VALUE: &str =
"d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2";
async fn init_handshake(server_cert: &rcgen::Certificate) -> Result<(tempfile::TempDir, PathBuf)> {
// https://github.com/hashicorp/go-plugin/blob/8d2aaa458971cba97c3bfec1b0380322e024b514/docs/internals.md
let _ = env::var("PLUGIN_MIN_PORT")
.wrap_err("PLUGIN_MIN_PORT not found")?
.parse::<u16>()
.wrap_err("PLUGIN_MIN_PORT not an int")?;
let _ = env::var("PLUGIN_MAX_PORT")
.wrap_err("PLUGIN_MAX_PORT not found")?
.parse::<u16>()
.wrap_err("PLUGIN_MAX_PORT not an int")?;
let tmpdir = tempfile::TempDir::new().wrap_err("failed to create temporary directory")?;
let socket = tmpdir.path().join("plugin");
// https://github.com/hashicorp/go-plugin/blob/8d2aaa458971cba97c3bfec1b0380322e024b514/server.go#L426
const CORE_PROTOCOL_VERSION: u8 = 1;
const PROTO_VERSION: u8 = 6;
let listener_addr_network = "unix";
let listener_addr = socket.display();
let proto_type = "grpc";
let b64_cert = base64::prelude::BASE64_STANDARD_NO_PAD.encode(server_cert.der());
println!("{CORE_PROTOCOL_VERSION}|{PROTO_VERSION}|{listener_addr_network}|{listener_addr}|{proto_type}|{b64_cert}");
Ok((tmpdir, socket))
} }
/// Private, only for use for with the derive macro. /// Private, only for use for with the derive macro.
#[doc(hidden)] #[doc(hidden)]
pub mod __derive_private { pub mod __derive_private {
pub use crate::framework::{ pub use crate::values::{Value, ValueKind};
pub use crate::{
AttrPath, AttrPathSegment, BaseValue, DResult, Diagnostic, Diagnostics, ValueModel, AttrPath, AttrPathSegment, BaseValue, DResult, Diagnostic, Diagnostics, ValueModel,
}; };
pub use crate::values::{Value, ValueKind};
pub use {Clone, Option::Some, Result::Err, ToOwned}; pub use {Clone, Option::Some, Result::Err, ToOwned};
pub fn new_object<const N: usize>(elems: [(&str, Value); N]) -> Value { pub fn new_object<const N: usize>(elems: [(&str, Value); N]) -> Value {

View file

@ -1,4 +1,4 @@
use super::DataSource; use crate::datasource::DataSource;
pub trait Provider: Send + Sync { pub trait Provider: Send + Sync {
fn name(&self) -> String; fn name(&self) -> String;

View file

@ -1,9 +1,7 @@
use crate::{ use crate::{
framework::{ datasource::{self, Mode},
datasource::{self, Mode},
AttrPathSegment, Diagnostics,
},
values::Type, values::Type,
AttrPathSegment, Diagnostics,
}; };
use super::grpc::tfplugin6; use super::grpc::tfplugin6;

View file

@ -1,12 +1,20 @@
mod cert;
mod convert; mod convert;
mod grpc; mod grpc;
use std::collections::HashMap; use std::collections::HashMap;
use std::env;
use std::path::PathBuf;
use base64::Engine;
use eyre::{bail, Context};
use tokio::net::UnixListener;
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
use tonic::transport::{Certificate, ServerTlsConfig};
use tracing::info;
use crate::framework::datasource::DataSource; use crate::datasource::DataSource;
use crate::framework::provider::Provider; use crate::provider::Provider;
pub use grpc::plugin::grpc_controller_server::GrpcControllerServer; pub use grpc::plugin::grpc_controller_server::GrpcControllerServer;
pub use grpc::tfplugin6::provider_server::ProviderServer; pub use grpc::tfplugin6::provider_server::ProviderServer;
@ -86,3 +94,86 @@ struct Schemas {
data_sources: HashMap<String, tfplugin6::Schema>, data_sources: HashMap<String, tfplugin6::Schema>,
diagnostics: Vec<tfplugin6::Diagnostic>, diagnostics: Vec<tfplugin6::Diagnostic>,
} }
pub async fn serve(provider: &dyn Provider) -> 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);
let (server_identity, server_cert) =
cert::generate_cert().wrap_err("generating server certificate")?;
let (_tmpdir, socket) = match init_handshake(&server_cert).await {
Ok(addr) => addr,
Err(err) => {
println!("{:?}", err);
bail!("init error");
}
};
let tls = ServerTlsConfig::new()
.identity(server_identity)
.client_auth_optional(true) // ??? terraform doesn't send certs ???
.client_ca_root(client_cert);
info!("Listening on {}", socket.display());
let uds = UnixListener::bind(socket).wrap_err("failed to bind unix listener")?;
let uds_stream = tokio_stream::wrappers::UnixListenerStream::new(uds);
let shutdown = tokio_util::sync::CancellationToken::new();
let server = tonic::transport::Server::builder()
.tls_config(tls)
.wrap_err("invalid TLS config")?
.add_service(ProviderServer::new(ProviderHandler::new(
shutdown.clone(),
provider,
)))
.add_service(GrpcControllerServer::new(Controller {
shutdown: shutdown.clone(),
}))
.serve_with_incoming(uds_stream);
tokio::select! {
_ = shutdown.cancelled() => {}
result = server => {
result.wrap_err("failed to start server")?;
}
}
Ok(())
}
const _MAGIC_COOKIE_KEY: &str = "TF_PLUGIN_MAGIC_COOKIE";
const _MAGIC_COOKIE_VALUE: &str =
"d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2";
async fn init_handshake(
server_cert: &rcgen::Certificate,
) -> eyre::Result<(tempfile::TempDir, PathBuf)> {
// https://github.com/hashicorp/go-plugin/blob/8d2aaa458971cba97c3bfec1b0380322e024b514/docs/internals.md
let _ = env::var("PLUGIN_MIN_PORT")
.wrap_err("PLUGIN_MIN_PORT not found")?
.parse::<u16>()
.wrap_err("PLUGIN_MIN_PORT not an int")?;
let _ = env::var("PLUGIN_MAX_PORT")
.wrap_err("PLUGIN_MAX_PORT not found")?
.parse::<u16>()
.wrap_err("PLUGIN_MAX_PORT not an int")?;
let tmpdir = tempfile::TempDir::new().wrap_err("failed to create temporary directory")?;
let socket = tmpdir.path().join("plugin");
// https://github.com/hashicorp/go-plugin/blob/8d2aaa458971cba97c3bfec1b0380322e024b514/server.go#L426
const CORE_PROTOCOL_VERSION: u8 = 1;
const PROTO_VERSION: u8 = 6;
let listener_addr_network = "unix";
let listener_addr = socket.display();
let proto_type = "grpc";
let b64_cert = base64::prelude::BASE64_STANDARD_NO_PAD.encode(server_cert.der());
println!("{CORE_PROTOCOL_VERSION}|{PROTO_VERSION}|{listener_addr_network}|{listener_addr}|{proto_type}|{b64_cert}");
Ok((tmpdir, socket))
}

View file

@ -6,7 +6,7 @@ use std::{
io::{self, Read}, io::{self, Read},
}; };
use crate::framework::{BaseValue, DResult, Diagnostic}; use crate::{BaseValue, DResult, Diagnostic};
#[derive(Debug)] #[derive(Debug)]
pub enum Type { pub enum Type {