-
Notifications
You must be signed in to change notification settings - Fork 315
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 ServiceConfig into RenderContext in supervisor #2226
Conversation
Thanks for the pull request! Here is what will happen next:
Thank you for contributing! |
e817035
to
b0670d9
Compare
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.
Excelent work! Some minor things.
@@ -289,7 +289,6 @@ pub mod fs; | |||
pub mod http_gateway; | |||
pub mod manager; | |||
pub mod output; | |||
pub mod supervisor; |
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 like this a lot!
@@ -48,16 +49,16 @@ use serde; | |||
use serde_json; | |||
use time::{self, Timespec, Duration as TimeDuration}; | |||
|
|||
pub use manager::service::{Service, ServiceConfig, ServiceSpec, UpdateStrategy, Topology}; | |||
use self::service::{DesiredState, StartStyle}; | |||
pub use self::service::{Service, ServiceSpec, UpdateStrategy, Topology}; |
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 we organize the pub use
imports together, perhaps at the bottom of the use
declarations. This would improve visibility into what is actually being exported like we put all the pub
fields at the top of a struct.
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.
The way I've organized these are
- Imports from std
- Public imports from extern
- Imports from extern
- Public imports from our own crate
- Imports from our own crate
I chose this since the rest of the code is formatted (or should be... we really need to commit some rules to the wiki!)
- Imports
- Public Const
- Private Const
- Public Static
- Private Static
- Public Trait
- Private Trait
- Public Struct
- Private Struct
- Public Function
- Private Function
We've got a few other small things in there like recently organizing members of the struct into public/private sections and organization of functions within the struct itself.
If you think we can come up with something more readable then let's go ahead and do it. Maybe it should be:
- Imports from std
- Public imports from extern
- Public Imports from our own crate
- Imports from extern
- Imports from our own crate
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.
Okay this isn't documented nor adhered to consistently. I'm fine leaving it as is but we need to start a style guide to communicate 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.
Yeah we really do, I'll put that on my list of things to do for Friday
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 guess this was part of our unspoken conventions that we're taking from Aaron Turon's Rust Guidelines book.
@@ -144,7 +141,7 @@ pub trait Hook: fmt::Debug + Sized { | |||
|
|||
fn path(&self) -> &Path; | |||
|
|||
fn template(&self) -> &Template; | |||
fn registry(&self) -> &Registry; |
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 really licking the name Registry
when I read this code (the whole file). Registry is a very generic name that (for me) doesn't really give me any useful information about what it actually does.
I guess its a kind of container but perhaps we could be more specific (HookContainer
), but then it also has behaviour (later on I see registry.render()
so perhaps its a kind of HookRenderer
. I don't know but currently it doesn't read nicely to me.
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.
Yeah I was a 2/10 on this name but it was a hell of a lot better than Template which is what is actually being registered to this thing. I was also thinking Renderer
so maybe Template
was a bad name but TemplateRenderer
is spot on. I'll make that change
use std::fmt; | ||
use std::fs::File; | ||
use std::io::BufWriter; | ||
use std::io::prelude::*; | ||
use std::path::{Path, PathBuf}; | ||
use std::result; | ||
use std::str::FromStr; | ||
use std::sync::{Arc, RwLock, RwLockReadGuard}; | ||
use std::sync::Arc; |
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.
Cool that this is simplified.
pub depot_url: String, | ||
pub spec_file: PathBuf, | ||
pub spec_ident: PackageIdent, | ||
pub start_style: StartStyle, | ||
pub topology: Topology, | ||
pub update_strategy: UpdateStrategy, | ||
pub cfg: Cfg, | ||
pub pkg: Pkg, | ||
pub sys: Arc<Sys>, |
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.
Why is Sys behind an Arc? Shouldn't it be immutable after start up? Or is this just to save memory instead of cloning?
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.
Ya this is just to save on memory instead of cloning. An Arc
is basically a read-only thread safe pointer. This is totally immutable ;)
ElectionStatus::None => { | ||
if self.last_election_status != *current_election_status { | ||
if self.last_election_status != census_group.topology_election_status { |
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.
nice improvement
if let Some(err) = self.copy_run().err() { | ||
outputln!(preamble self.service_group, "Failed to copy run hook: {}", err); | ||
{ | ||
let ctx = self.render_context(&census_ring); |
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.
If this can be avoided and run in the update_configuration
method I would find that much better.
Perhaps there could be code like:
if !self.initialized {
... always render everything
return
}
in the update_configuration
method.
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.
Yeah you're right, this actually can completely be avoided. It's already being called in the update_templates
function that I've created from refactored bits of update_configuration
. Gonna make those changes and push them up!
if !self.initialized { | ||
self.initialize(); | ||
self.initialize(census_ring); |
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.
Already commented on this.
} | ||
|
||
#[derive(Clone, Debug, Serialize)] | ||
pub struct BindGroup; |
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.
Empty? I guess this is WIP.
components/sup/src/templating/mod.rs
Outdated
|
||
impl Template { | ||
impl Registry { |
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.
Already commented on this from the POV of the client code in hooks.rs
269bb72
to
95c085d
Compare
ffe4de7
to
ce6f986
Compare
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 is looking pretty good to me!
"libsodium-sys 0.0.14 (registry+https://github.com/rust-lang/crates.io-index)", | ||
"serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)", | ||
"libsodium-sys 0.0.14 (git+https://github.com/dnaq/sodiumoxide)", | ||
"serde 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", |
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.
Wow, gotta keep up, right?
pub struct SysInfo { | ||
pub ip: String, | ||
pub ip: IpAddr, |
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.
🥇
let mut reader = BufReader::new(file); | ||
hash_reader(&mut reader) | ||
} | ||
|
||
pub fn hash_string(data: &str) -> Result<String> { | ||
pub fn hash_string(data: &str) -> String { |
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.
That's funny, I wonder if there used to be a possible error here?
components/sup/src/census.rs
Outdated
pub struct CensusMember { | ||
pub member_id: MemberId, | ||
pub pkg: Option<PackageIdent>, | ||
|
||
service: String, | ||
group: String, | ||
org: Option<String>, | ||
cfg: toml::value::Table, | ||
#[serde( |
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.
That's kickass!
@@ -51,8 +51,7 @@ use sup::command; | |||
use sup::http_gateway; | |||
use sup::manager::{Manager, ManagerConfig, ServiceStatus}; | |||
use sup::manager::service::{DesiredState, ServiceBind, Topology, UpdateStrategy}; | |||
use sup::manager::service::{ServiceSpec, StartStyle}; | |||
use sup::supervisor::ProcessState; | |||
use sup::manager::service::{ProcessState, ServiceSpec, StartStyle}; |
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.
That's probably a good sign!
@@ -48,16 +49,16 @@ use serde; | |||
use serde_json; | |||
use time::{self, Timespec, Duration as TimeDuration}; | |||
|
|||
pub use manager::service::{Service, ServiceConfig, ServiceSpec, UpdateStrategy, Topology}; | |||
use self::service::{DesiredState, StartStyle}; | |||
pub use self::service::{Service, ServiceSpec, UpdateStrategy, Topology}; |
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 guess this was part of our unspoken conventions that we're taking from Aaron Turon's Rust Guidelines book.
member, | ||
Trace::default(), | ||
ring_key, | ||
None, | ||
Some(&fs_cfg.data_path), | ||
Box::new(SuitabilityLookup(services.clone())))?; | ||
outputln!("Butterfly Member ID {}", server.member_id()); | ||
outputln!("Supervisor Member-ID {}", sys.member_id); |
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.
That's a nice tweak and more clearly communicate with the extra internal jargon.
components/sup/src/manager/mod.rs
Outdated
member.set_persistent(sys.permanent); | ||
// JW TODO: Should we really be setting these here? Seems like they should be auto set | ||
// by the butterfly server constructor | ||
member.set_swim_port(sys.gossip_port as i32); |
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.
That is a bit surprising, but works for the moment!
@@ -138,7 +139,9 @@ impl fmt::Display for Error { | |||
format!("Invalid keypath: {}. Specify an absolute path to a file on disk.", | |||
e) | |||
} | |||
Error::ConfigFileIO(ref e) => format!("Error reading configuration file: {}", e), | |||
Error::ConfigFileIO(ref f, ref e) => { | |||
format!("Error reading configuration file, {}, {}", f.display(), 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.
nice. Always a bummer when you have no idea what file the error is about
pub preamble: String, | ||
pub state: ProcessState, | ||
pub state_entered: Timespec, | ||
pub has_started: bool, | ||
pub runtime_config: RuntimeConfig, | ||
pid: Option<PathBuf>, |
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 don't see anywhere that sets this except where we ser it to None
when removing the pid file.
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.
We use this in create_pidfile
to record what the path is before we delete it in cleanup_pidfile
. After we do that, we set it to None
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.
that function uses child.id()
(as it should) I do not see any use of self.pid
there. I might just be being my usual dense self here.
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.
It's on line 214, 224, and 231
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.
oh totally missed 214. pid_file_path
might be a better name but not a huge deal.
@@ -1,162 +0,0 @@ | |||
// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors |
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.
great move to separate out the helpers.
if self.updater | ||
.check_for_updated_package(service, &self.census_ring) { | ||
service.populate(&self.census_ring); | ||
self.gossip_latest_service_rumor(&service); |
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.
hmm. did you find this gossiping was not necessary? I'd think it would be something you would want to rumor out but maybe something else takes care of the rumor. Just curious about the rationale.
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.
Yep! This should be completely unnecessary. We are going to call tick()
pretty much right after this on the service which is going to tell us that it updated. Once it does, we will call gossip_latest_service_rumor()
as a very next step
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.
ahh that makes sense
Oh just found a bug here that kills status deserialization. I can add a fix to this branch. |
@mwrock the
You must use the block syntax ( |
eff057c
to
7f5dcf4
Compare
The spirit of this commit is to improve reliability when compiling templated plan artifacts such as configuration files and hooks. We accomplish this by adding a `RenderContext` module which is used in the templating module to bind data to templates. This should improve overall readability of our code and also ensure that we have a typed structure when deserializing data from handlebars in the `templating::helpers` module. We leverage a zero-copy deserialization implementation in `RenderContext` to ensure that we don't duplicate data for each helper in a template. * Add `templating::context` module and `RenderContext` struct. This is what is sent to our templating engine to be bound to all templates. * Add `manager::sys` module and `Sys` struct. This represents the system as a whole and is eventually serialized in the RenderContext as `sys` * Move http_listen to `Sys` struct from `Manager` struct * Move gossip_listen to `Sys` struct `Manager` struct * Add `Arc<Sys>` to `Service` struct. This holds a read only reference to the `Sys` struct of the `Manager` which loaded it. This ensures that we only allocate one `Sys` struct which doesn't change unless the Supervisor is restarted * Rewrote `manager::service::config` module and added `Cfg` struct to it. This contains all configuration logic for the service and is passed to the `RenderContext` in the same way as the `Sys` struct * Add `manager::service::package` module and `Pkg` struct to it. This is similar to both the `Sys` and `Cfg` modules in it's relation with `RenderContext`. It contains all information about the package the service is running * Moved `supervisor` module to `manager::service::supervisor` * Fix serialization/deserialization issues with SysInfo struct * Fix API for `crypto::hash` - only return a `Result` if there is actually an error case * Move command execution functions from the `util` module to `manager::service::exec`. The `create_command` function from that module has been renamed to `run_cmd` and now accepts a reference to a `Pkg` struct which will ensure that all commands are executed within the context of the service's package * Remove `gossip.toml` and `config.toml` cache files from service directory. We no longer need to write these out since we now persist and reload our entire rumor store on supervisor boot. * Improve error when loading a bad package from a package install * Upgrade completely to serde 1.0+ * Temporarily switch to git source for libsodium dependency * Replace "Butterfly" in supervisor output with "Supervisor" or another more appropriate description which hides our implementation details * Add function and module documentation Signed-off-by: Jamie Winsor <jamie@vialstudios.com>
Signed-off-by: Matt Wrock <matt@mattwrock.com>
added a fix for the deserialization bug. Will unleash sentinals when green |
@thesentinels approve |
🤘 I am testing your branch against master before merging it. We do this to ensure that the master branch is never failing tests. |
Travis CI has started testing this PR. |
💖 Travis CI reports this PR passed. It always makes me feel nice when humans approve of one anothers work. I'm merging this PR now. I just want you and the contributor to answer me one question: |
Refactor ServiceConfig into RenderContext in Habitat Supervisor
The spirit of this commit is to improve reliability when compiling templated
plan artifacts such as configuration files and hooks. We accomplish this by
adding a
RenderContext
module which is used in the templating module tobind data to templates. This should improve overall readability of our code
and also ensure that we have a typed structure when deserializing data from
handlebars in the
templating::helpers
module. We leverage a zero-copydeserialization implementation in
RenderContext
to ensure that we don'tduplicate data for each helper in a template.
templating::context
module andRenderContext
struct. This is whatis sent to our templating engine to be bound to all templates.
manager::sys
module andSys
struct. This represents the system asa whole and is eventually serialized in the RenderContext as
sys
Sys
struct fromManager
structSys
structManager
structArc<Sys>
toService
struct. This holds a read only reference tothe
Sys
struct of theManager
which loaded it. This ensures that weonly allocate one
Sys
struct which doesn't change unless the Supervisoris restarted
manager::service::config
module and addedCfg
struct to it.This contains all configuration logic for the service and is passed
to the
RenderContext
in the same way as theSys
structmanager::service::package
module andPkg
struct to it. This issimilar to both the
Sys
andCfg
modules in it's relation withRenderContext
. It contains all information about the package the serviceis running
supervisor
module tomanager::service::supervisor
crypto::hash
- only return aResult
if there is actuallyan error case
util
module tomanager::service::exec
. Thecreate_command
function from that module hasbeen renamed to
run_cmd
and now accepts a reference to aPkg
structwhich will ensure that all commands are executed within the context of the
service's package
gossip.toml
andconfig.toml
cache files from service directory.We no longer need to write these out since we now persist and reload our
entire rumor store on supervisor boot.
appropriate description which hides our implementation details