From 72788bed667cb69f543d860ab79f76482044e3af Mon Sep 17 00:00:00 2001 From: Dotan Nahum Date: Sun, 19 May 2024 16:39:52 +0300 Subject: [PATCH] finalize etcd impl --- teller-providers/src/providers/etcd.rs | 52 ++++++++++++++++++-------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/teller-providers/src/providers/etcd.rs b/teller-providers/src/providers/etcd.rs index 301060e8..dc5639d8 100644 --- a/teller-providers/src/providers/etcd.rs +++ b/teller-providers/src/providers/etcd.rs @@ -1,12 +1,12 @@ -//! Hashicorp Consul +//! etcd //! //! //! ## Example configuration //! //! ```yaml //! providers: -//! consul1: -//! kind: hashicorp_consul +//! etcd1: +//! kind: etcd //! # options: ... //! ``` //! ## Options @@ -15,7 +15,7 @@ //! use async_trait::async_trait; -use etcd_client::{Client, DeleteOptions, GetOptions, KvClient}; +use etcd_client::{Client, ConnectOptions, DeleteOptions, GetOptions}; use serde_derive::{Deserialize, Serialize}; use tokio::sync::Mutex; @@ -25,11 +25,20 @@ use crate::{ Error, Provider, Result, }; +/// Etcd Options +/// #[allow(clippy::module_name_repetitions)] #[derive(Default, Serialize, Deserialize, Debug, Clone)] pub struct EtcdOptions { /// Etcd address. pub address: Option, + + /// An optional 'user' field, which includes a user name and password separated by `:`. + /// + /// Example: `joe:rootpass` + /// + /// see more: [on this github thread](https://github.com/etcd-io/etcd/issues/10338#issuecomment-448280528) + pub user: Option, } pub struct Etcd { @@ -40,14 +49,9 @@ pub struct Etcd { fn to_err(_pm: &PathMap, err: etcd_client::Error) -> Error { Error::Any(Box::new(err)) } -async fn create_client() -> Result { - Ok(Client::connect(["127.0.0.1:2379"], None) - .await - .map_err(|err| Error::CreateProviderError(err.to_string()))?) -} impl Etcd { - /// Create a new hashicorp Consul + /// Create a new etcd provider /// /// # Errors /// @@ -60,9 +64,26 @@ impl Etcd { .as_ref() .ok_or_else(|| Error::Message("address not present.".to_string()))?; + let user = opts + .user + .or_else(|| std::env::var("ETCDCTL_USER").ok()) + .map(|user| { + user.split_once(':') + .ok_or_else(|| { + Error::Message("user field should be in `user:pwd` format".to_string()) + }) + .map(|(user, pwd)| (user.to_string(), pwd.to_string())) + }) + .transpose()?; + + let mut connect_opts = ConnectOptions::new(); + if let Some(user) = user { + connect_opts = connect_opts.with_user(user.0, user.1); + } + Ok(Self { client: Mutex::new( - Client::connect([address], None) + Client::connect([address], Some(connect_opts)) .await .map_err(|err| Error::CreateProviderError(err.to_string()))?, ), @@ -81,7 +102,7 @@ impl Provider for Etcd { } async fn get(&self, pm: &PathMap) -> Result> { - let mut client = create_client().await?; + let mut client = self.client.lock().await.kv_client(); let res = if pm.keys.is_empty() { client @@ -131,7 +152,7 @@ impl Provider for Etcd { } async fn put(&self, pm: &PathMap, kvs: &[KV]) -> Result<()> { - let mut client = create_client().await?; + let mut client = self.client.lock().await.kv_client(); for kv in kvs { client .put( @@ -148,7 +169,7 @@ impl Provider for Etcd { } async fn del(&self, pm: &PathMap) -> Result<()> { - let mut client = create_client().await?; + let mut client = self.client.lock().await.kv_client(); if pm.keys.is_empty() { client .delete( @@ -182,7 +203,7 @@ mod tests { #[test_log::test] #[cfg(not(windows))] fn sanity_test() { - use std::{collections::HashMap, env, time::Duration}; + use std::{collections::HashMap, env}; use dockertest::{waitfor, Composition, DockerTest, Image}; @@ -229,6 +250,7 @@ mod tests { "etcd", Some(EtcdOptions { address: Some(address), + user: None, }), ) .await