-
Notifications
You must be signed in to change notification settings - Fork 276
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[refactor] #2118: WIP: Default
config
#2583
Conversation
Signed-off-by: Ilia Churin <[email protected]>
Default
configDefault
config
/// from the length of the array in | ||
/// [`self.sumeragi.trusted_peers.peers`], most likely due to two | ||
/// (or more) peers having the same public key. | ||
fn from_path<P: AsRef<Path> + Debug + Clone>(path: P) -> eyre::Result<Self, eyre::Error> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't know how robust it is to try to make a default from_path
method for all proxy structs, but there definitely is some space for improvement in existing code, as we have 3 or 4 implementations of almost the same function scattered as impl Configuration
here and there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be a separate trait. I'd say LoadFromDisk
and make it a little richer.
@@ -209,7 +210,7 @@ mod docs { | |||
|
|||
use super::*; | |||
|
|||
impl<E: Debug, C: Configurable<Error = E> + Send + Sync + Default> PrintDocs for C {} | |||
impl<E: Debug, C: Configurable<Error = E> + Send + Sync + DocsDefault> PrintDocs for C {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seemed to work as a stopgap when we take away the #[serde(default)]
on config structs, but we need something more sophisticated here. That would be done at a later stage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Create a ticket, reference in // TODO
.
.wrap_err(format!("Failed to deserialize json {:?} from reader", path)) | ||
} | ||
|
||
// fn finalize(&mut self) -> Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left commented code here as it marks an important point: even with the public/private keys being most important non-defaultable config parameters, they probably don't need to be specified in all the overlapping structures. I.e. it could always be enough to specify it either in torii
or iroha
config only, and then propagate it into the empty one. Something evolved from this func could be a mechanism to help that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I'd add this to the front of the PR and to the description of the ticket that fixes it.
Also consider making it a doc-test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should consider making keys it's own thing and then pass independently to parts of system that reqiures them?
Like we have Secrets
and ConfigMaps
in k8s.
} else if is_option { | ||
quote! { | ||
#l_value = Some(serde_json::from_value(var.into()) | ||
.map_err(|e| iroha_config_base::derive::Error::field_error(stringify!(#ident), e))?) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code path should get run when deriving Configurable
for our Proxy
structs.
quote! { | ||
// <#inner>::load_environment()?; | ||
#l_value.unwrap().load_environment()?; | ||
// if let Ok(config) = <#inner>::load_environment() { | ||
// #l_value = Some(config) | ||
// }; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An unfinished mess here as the whole Configurable
trait is not implemented yet for proxy and I wasn't able to test this piece. What gets done to load the inner configuration of fields wrapped inside Option
still needs to be thought through.
@@ -8,7 +8,8 @@ use std::{panic, path::PathBuf, sync::Arc}; | |||
|
|||
use color_eyre::eyre::{eyre, Result, WrapErr}; | |||
use iroha_actor::{broker::*, prelude::*}; | |||
use iroha_config::iroha::Configuration; | |||
use iroha_config::base::proxy::DocsDefault; | |||
use iroha_config::iroha::{Configuration, ConfigurationProxy}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use iroha_config::iroha::{Configuration, ConfigurationProxy}; | |
use iroha_config::iroha::{Configuration, ConfigurableProxy}; |
@@ -209,7 +210,7 @@ mod docs { | |||
|
|||
use super::*; | |||
|
|||
impl<E: Debug, C: Configurable<Error = E> + Send + Sync + Default> PrintDocs for C {} | |||
impl<E: Debug, C: Configurable<Error = E> + Send + Sync + DocsDefault> PrintDocs for C {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Create a ticket, reference in // TODO
.
@@ -53,7 +54,7 @@ view! { | |||
} | |||
} | |||
|
|||
impl Default for Configuration { | |||
impl DocsDefault for Configuration { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a huge fan of DocsDefault
for a trait name. Maybe ReferenceDefault
? ExampleDefault
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, the point is that we shouldn't allow people anything resembling a Default
trait. The point is to prevent this kind of usage, which we can achieve by
- enforcing that the default is either implemented only for the proxy structure, and no concrete
Configuration
impl Default
which we can check. - By encoding the default values in place where they're used. So there isn't a general
Default
trait, just an example struct instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe something like trait Example
with method fn example() -> Self
?
use super::*; | ||
|
||
/// Pseudo-default trait only used for doc generation | ||
pub trait DocsDefault { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which is very much the exact same thing as Default
, can be used in the same way, and people will probably abuse in the same way.
We need to get rid not only of core::Default
but also of any trait that can be used like it. Adding a DocsDefault
trait in the exact same spot doesn't solve anything.
/// from the length of the array in | ||
/// [`self.sumeragi.trusted_peers.peers`], most likely due to two | ||
/// (or more) peers having the same public key. | ||
fn from_path<P: AsRef<Path> + Debug + Clone>(path: P) -> eyre::Result<Self, eyre::Error> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be a separate trait. I'd say LoadFromDisk
and make it a little richer.
.wrap_err(format!("Failed to deserialize json {:?} from reader", path)) | ||
} | ||
|
||
// fn finalize(&mut self) -> Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I'd add this to the front of the PR and to the description of the ticket that fixes it.
Also consider making it a doc-test.
.wrap_err(format!("Failed to deserialize json {:?} from reader", path)) | ||
} | ||
|
||
// fn finalize(&mut self) -> Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// fn finalize(&mut self) -> Result<()> { | |
// fn finish(&mut self) -> Result<()> { |
"-ize" in names can confuse users. Rust uses the "-ize" in all of its docs. Our compny policy is to use "-ise". To avoid confusion, use a word that has neither.
@@ -137,6 +137,7 @@ impl Configuration { | |||
AccountId::from_str("alice@wonderland").expect("Account ID not valid") | |||
} | |||
|
|||
// TODO: Delete this? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just remember to do it.
.wrap_err(format!("Failed to deserialize json {:?} from reader", path)) | ||
} | ||
|
||
// fn finalize(&mut self) -> Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should consider making keys it's own thing and then pass independently to parts of system that reqiures them?
Like we have Secrets
and ConfigMaps
in k8s.
@@ -13,6 +15,7 @@ pub mod derive { | |||
/// View contains a subset of the fields that the type has. | |||
/// | |||
/// Works only with structs. | |||
// TODO: alter as won't be true after yeeting [`Default`] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I belive that we already can drop Default
requirement and remove part of macro that produce Default
for View
.
Since we have only one way conversation Config -> View
.
@@ -53,7 +54,7 @@ view! { | |||
} | |||
} | |||
|
|||
impl Default for Configuration { | |||
impl DocsDefault for Configuration { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe something like trait Example
with method fn example() -> Self
?
quote! { | ||
// <#inner>::load_environment()?; | ||
#l_value.unwrap().load_environment()?; | ||
// if let Ok(config) = <#inner>::load_environment() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#l_value = <#inner>::load_enviroment().ok()
I suggest closing this PR in favour of the real PRs. |
Signed-off-by: Ilia Churin [email protected]
Description of the Change
Combine
iniroha_config_base::proxy
and its corresponding erroriroha_config_base::derive
crate into modulesProxy
derive macroConfigurable
trait to make it more readableProxy
completely conform toConfigurable
and implement all its methodsDocsDefault
to makekagami
work for the time beingAll of these changes won't probably go into a final real PR (quite likely nothing in its current form besides the crate refactor), so instead this draft may serve as a ground for comments that can become guidelines for my future work. Moreover, the code doesn't even compile at the time of writing due to the unfinished point 5 from the list above.
Issue
Was part of work towards #2118, but in a discussion with @appetrosyan it was decided to make it an epic and split it into more smaller ones to make the progress more trackable.
The epic for tracking: #2585.
Benefits
Ideally, the end result should not only allow flexible loading of configuration, but also give informative messages/suggestions in case of failure. E.g. if no keys were provided, suggest a command to generate them (or have a flag for generation on the fly). Moreover, it wouldn't hurt if at start Iroha logs explicitly, which parts of configs are loaded as default values due to them not being provided.
Possible Drawbacks
Current design of
Configurable
trait suffers from interface multiplicity, that is, it serves two functions at once which should eventually be decoupled: things pertaining to configuration loading and initialization, and all the things related to querying it or modifying parameters at runtime.Alternate Designs
Not necessarily alternate designs, but a few things mentioned in previous discussion:
ConstStr
.ConfigProxy
being wrapped inOption
to signify the possibility of them being uninitialized, there are some fields which have to be initialized eventually, and those that don't and can instead rely on the default value. This distinction could be marked by having a separate struct for the non-defaultable option fields.