-
Notifications
You must be signed in to change notification settings - Fork 314
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
extract supervisor templating and execute an install hook when installing packages #5866
Conversation
Thanks for the pull request! Here is what will happen next:
Thank you for contributing! |
@@ -12,16 +12,17 @@ | |||
// See the License for the specific language governing permissions and |
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 weird that git thinks this is a rename of components/sup/src/sys/windows/abilities.rs because it certainly is not.
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 just GitHub is confused? My local git doesn't see this as a rename:
➤ git diff --name-status 2af19c74cd7653ded563f7ef1f5997d18a53216e
M Cargo.lock
M components/common/src/command/package/install.rs
M components/pkg-export-docker/defaults/Dockerfile.hbs
M components/pkg-export-docker/defaults/Dockerfile_win.hbs
M components/pkg-export-docker/src/build.rs
M components/pkg-export-docker/src/docker.rs
2d72ece
to
e57b63e
Compare
related: #5161 |
Note I plan to add to the docs before this is merged but would like some review first in case that changes the direction of the docs. |
Review in progress. Should finish tomorrow. |
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.
Mostly optional readability feedback, but I do feel pretty strongly about renaming InstallHookMode
and it's variants to improve clarity.
@@ -197,6 +200,32 @@ impl Default for InstallMode { | |||
} | |||
} | |||
|
|||
#[derive(Debug, Eq, PartialEq)] | |||
pub enum InstallHookMode { | |||
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.
We might want to give this a more illustrative name like RunOnce
or something. Plus, Default
is easy to get confused with the trait name 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.
Yeah, that's a great point re: "Default"
@@ -12,16 +12,17 @@ | |||
// See the License for the specific language governing permissions and |
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 just GitHub is confused? My local git doesn't see this as a rename:
➤ git diff --name-status 2af19c74cd7653ded563f7ef1f5997d18a53216e
M Cargo.lock
M components/common/src/command/package/install.rs
M components/pkg-export-docker/defaults/Dockerfile.hbs
M components/pkg-export-docker/defaults/Dockerfile_win.hbs
M components/pkg-export-docker/src/build.rs
M components/pkg-export-docker/src/docker.rs
0ca73b4
to
6dc1860
Compare
Most recent commits bring back the supervisor specific hooks and |
There are 2 key areas where I deviated from some changes @christophermaier and I discussed:
|
This is ready for review again. After using/testing the feature more extensively and some discussions with @christophermaier I have made the following changes:
|
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.
Mostly small things, but there are some correctness issues I think we should address.
components/hab/src/main.rs
Outdated
@@ -670,6 +672,13 @@ fn sub_pkg_install(ui: &mut UI, m: &ArgMatches) -> Result<()> { | |||
LocalPackageUsage::default() | |||
}; | |||
|
|||
let install_hook_mode = | |||
if !feat::is_enabled(feat::InstallHook) || m.is_present("IGNORE_INSTALL_HOOK") { |
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 !feat::is_enabled(feat::InstallHook) || m.is_present("IGNORE_INSTALL_HOOK") { | |
if m.is_present("IGNORE_INSTALL_HOOK") { |
The argument is only available if the feature is enabled, so there's no way is_present
could return true otherwise. If you try to add it without the feature being enabled, you get an error before ever reaching this code:
➤ ./target/debug/hab pkg install --ignore-install-hook core/redis
error: Found argument '--ignore-install-hook' which wasn't expected, or isn't valid in this context
if let Ok(package) = | ||
PackageInstall::load(&service.pkg.ident, Some(Path::new(&*FS_ROOT_PATH))) | ||
{ | ||
if let Err(err) = common::command::package::install::check_install_hooks( |
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 based on your comment here, correct?
Some scenarios may not call
common::command::package::install::start
when first loading a package. The 2 scenarios where this is the case are packages built in a studio and packages loaded from a spec file by the supervisor. There is now a publiccheck_install_hooks
function that can be called to run all install hooks of a package and its TDEPS.
Since add_service
is called to synchronize the supervisor state with the spec files, what happens if a service is loaded with --ignore-install-hook
, then the supervisor is restarted and it runs this code based on the spec file? Since there's no INSTALL_HOOK_STATUS_FILE
it seems like run_install_hook_when_failed
would run the install hook, but that seems contrary to the user's intent.
Relatedly, run_install_hook_when_failed
seems to be a bit of a misnomer. It's more like run_install_hook_unless_already_successful
.
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.
--ignore-install-hook
is intentionally only available on hab pkg install
and not on hab svc load
. In a supervisor context any install hook should be run if provided otherwise It is safe to assume the service would fail.
Definitely agree on the function name. I never felt great about it and I like your suggested name.
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.
Ok, that makes sense. I think a comment to that effect would be useful for posterity.
@@ -1970,6 +1974,27 @@ do_default_build_config() { | |||
done | |||
chmod 755 "$pkg_prefix"/config | |||
fi | |||
if [[ "${HAB_FEAT_INSTALL_HOOK:-}" = "true" && -d "$PLAN_CONTEXT/config_install" ]]; then |
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 [[ "${HAB_FEAT_INSTALL_HOOK:-}" = "true" && -d "$PLAN_CONTEXT/config_install" ]]; then | |
if [[ -n "${HAB_FEAT_INSTALL_HOOK:-}" && -d "$PLAN_CONTEXT/config_install" ]]; then |
The features environment variables consider anything non-null and non-empty to be true
find "$PLAN_CONTEXT/config_install" "$find_exclusions" | while read -r FILE | ||
do | ||
if [[ -d "$FILE" ]]; then | ||
mkdir -p "$pkg_prefix${FILE#$PLAN_CONTEXT}" |
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 consider myself fairly familiar with bash and yet still had to look it up to understand what ${parameter#word}
does. Especially since it's used twice, I'd suggest an intermediate variable to make things a bit clearer:
local plan_context_relative_path="$pkg_prefix${FILE#$PLAN_CONTEXT}"
.as_ref() | ||
.join("hab") | ||
.join("user") | ||
.join(ctx.primary_svc_ident().clone().name) |
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.
There's no need to clone here
.join(ctx.primary_svc_ident().clone().name) | |
.join(&ctx.primary_svc_ident().name) |
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.
Or, you could avoid the many join
s and just use one path:
let dst = rootfs.as_ref().join(format!(
"hab/user/{}/config/user.toml",
ctx.primary_svc_ident().name
));
According to the Path
docs, this should work on Windows
@@ -745,10 +745,10 @@ _install_dependency() { | |||
"${HAB_FEAT_IGNORE_LOCAL:-}" = "TRUE" ]]; then | |||
IGNORE_LOCAL="--ignore-local" | |||
fi | |||
$HAB_BIN install -u $HAB_BLDR_URL --channel $HAB_BLDR_CHANNEL ${IGNORE_LOCAL:-} "$dep" || { | |||
$HAB_BIN install -u $HAB_BLDR_URL --channel $HAB_BLDR_CHANNEL ${IGNORE_LOCAL:-} "$dep" "$2" || { |
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 can see this is to support possibly passing --ignore-install-hook
, but that's not very clear when viewed without the additional context of this PR. I'd suggest making this more general and nixing the local dep
on L738:
$HAB_BIN install -u $HAB_BLDR_URL --channel $HAB_BLDR_CHANNEL ${IGNORE_LOCAL:-} "$dep" "$2" || { | |
$HAB_BIN install -u $HAB_BLDR_URL --channel $HAB_BLDR_CHANNEL ${IGNORE_LOCAL:-} "$@" || { |
Ditto for L751.
@@ -1083,7 +1083,11 @@ _resolve_run_dependencies() { | |||
|
|||
# Append to `${pkg_deps_resolved[@]}` all resolved direct run dependencies. | |||
for dep in "${pkg_deps[@]}"; do | |||
_install_dependency "$dep" | |||
if [[ "${HAB_FEAT_INSTALL_HOOK:-}" = "true" ]]; then |
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 [[ "${HAB_FEAT_INSTALL_HOOK:-}" = "true" ]]; then | |
if [[ -n "${HAB_FEAT_INSTALL_HOOK:-}" ]]; then |
Obvious fix; these changes are the result of automation not creative thinking.
Some code cleanups after #5866
With #5866, we inadvertently broke the output of the HTTP gateway when retrieving health check hook information. The overall health of a service was still returned, but neither the standard output nor the standard error were included. Because the addition of `install` (and later, `unintsall`) hooks introduced a situation in which hooks could be executed without having a service group present (that is, outside of a running Supervisor process), the common logic for finding the path to the log output of hooks was changed to accept a `&str` rather than a `&ServiceGroup`. The value that is ultimately used should have only been the "service name" portion (e.g., "redis" and not "redis.default"). Unfortunately, because `ServiceGroup` implements `Deref<Target=String>`, the calls to `stdout_log_path` and `stderr_log_path` in the `http_gateway` module could continue to compile. Since `Deref` is automatically invoked, the compiler was able to coerce the `&ServiceGroup` reference all the way to a `&str`, thus satisfying the new code. However, this `&str` was the _full_ name of the `ServiceGroup` (e.g., "redis.default"), which ended up silently breaking this HTTP gateway functionality. Rather than looking for the files at `/hab/svc/$SERVICE/logs`, we were instead looking at `hab/svc/$SERVICE.$GROUP/logs`. Now, we explicitly call `service_group.service()` at this callsite, thus restoring the prior functionality. A "proper" fix would involve more extensive refactoring to ensure that we have stricter typing to ensure the right "kind" of string is making its way down to `stdout_log_path` and `stderr_log_path`, and possibly removing the `Deref<Target=String>` implementation for `ServiceGroup`, since there is clearly more than one kind of string-like thing we could conceivably want out of a `ServiceGroup`. Such an approach appears to get us rather far afield into the workings and usage of our `outputln!` macros, which is an entirely different can of worms that is best left unopened here (see #6584 for more on that). Signed-off-by: Christopher Maier <cmaier@chef.io>
With #5866, we inadvertently broke the output of the HTTP gateway when retrieving health check hook information. The overall health of a service was still returned, but neither the standard output nor the standard error were included. Because the addition of `install` (and later, `unintsall`) hooks introduced a situation in which hooks could be executed without having a service group present (that is, outside of a running Supervisor process), the common logic for finding the path to the log output of hooks was changed to accept a `&str` rather than a `&ServiceGroup`. The value that is ultimately used should have only been the "service name" portion (e.g., "redis" and not "redis.default"). Unfortunately, because `ServiceGroup` implements `Deref<Target=String>`, the calls to `stdout_log_path` and `stderr_log_path` in the `http_gateway` module could continue to compile. Since `Deref` is automatically invoked, the compiler was able to coerce the `&ServiceGroup` reference all the way to a `&str`, thus satisfying the new code. However, this `&str` was the _full_ name of the `ServiceGroup` (e.g., "redis.default"), which ended up silently breaking this HTTP gateway functionality. Rather than looking for the files at `/hab/svc/$SERVICE/logs`, we were instead looking at `hab/svc/$SERVICE.$GROUP/logs`. Now, we explicitly call `service_group.service()` at this callsite, thus restoring the prior functionality. A "proper" fix would involve more extensive refactoring to ensure that we have stricter typing to ensure the right "kind" of string is making its way down to `stdout_log_path` and `stderr_log_path`, and possibly removing the `Deref<Target=String>` implementation for `ServiceGroup`, since there is clearly more than one kind of string-like thing we could conceivably want out of a `ServiceGroup`. Such an approach appears to get us rather far afield into the workings and usage of our `outputln!` macros, which is an entirely different can of worms that is best left unopened here (see #6584 for more on that). Signed-off-by: Christopher Maier <cmaier@chef.io>
With #5866, we inadvertently broke the output of the HTTP gateway when retrieving health check hook information. The overall health of a service was still returned, but neither the standard output nor the standard error were included. Because the addition of `install` (and later, `unintsall`) hooks introduced a situation in which hooks could be executed without having a service group present (that is, outside of a running Supervisor process), the common logic for finding the path to the log output of hooks was changed to accept a `&str` rather than a `&ServiceGroup`. The value that is ultimately used should have only been the "service name" portion (e.g., "redis" and not "redis.default"). Unfortunately, because `ServiceGroup` implements `Deref<Target=String>`, the calls to `stdout_log_path` and `stderr_log_path` in the `http_gateway` module could continue to compile. Since `Deref` is automatically invoked, the compiler was able to coerce the `&ServiceGroup` reference all the way to a `&str`, thus satisfying the new code. However, this `&str` was the _full_ name of the `ServiceGroup` (e.g., "redis.default"), which ended up silently breaking this HTTP gateway functionality. Rather than looking for the files at `/hab/svc/$SERVICE/logs`, we were instead looking at `hab/svc/$SERVICE.$GROUP/logs`. Now, we explicitly call `service_group.service()` at this callsite, thus restoring the prior functionality. A "proper" fix would involve more extensive refactoring to ensure that we have stricter typing to ensure the right "kind" of string is making its way down to `stdout_log_path` and `stderr_log_path`, and possibly removing the `Deref<Target=String>` implementation for `ServiceGroup`, since there is clearly more than one kind of string-like thing we could conceivably want out of a `ServiceGroup`. Such an approach appears to get us rather far afield into the workings and usage of our `outputln!` macros, which is an entirely different can of worms that is best left unopened here (see #6584 for more on that). Signed-off-by: Christopher Maier <cmaier@chef.io>
With #5866, we inadvertently broke the output of the HTTP gateway when retrieving health check hook information. The overall health of a service was still returned, but neither the standard output nor the standard error were included. Because the addition of `install` (and later, `unintsall`) hooks introduced a situation in which hooks could be executed without having a service group present (that is, outside of a running Supervisor process), the common logic for finding the path to the log output of hooks was changed to accept a `&str` rather than a `&ServiceGroup`. The value that is ultimately used should have only been the "service name" portion (e.g., "redis" and not "redis.default"). Unfortunately, because `ServiceGroup` implements `Deref<Target=String>`, the calls to `stdout_log_path` and `stderr_log_path` in the `http_gateway` module could continue to compile. Since `Deref` is automatically invoked, the compiler was able to coerce the `&ServiceGroup` reference all the way to a `&str`, thus satisfying the new code. However, this `&str` was the _full_ name of the `ServiceGroup` (e.g., "redis.default"), which ended up silently breaking this HTTP gateway functionality. Rather than looking for the files at `/hab/svc/$SERVICE/logs`, we were instead looking at `hab/svc/$SERVICE.$GROUP/logs`. Now, we explicitly call `service_group.service()` at this callsite, thus restoring the prior functionality. A "proper" fix would involve more extensive refactoring to ensure that we have stricter typing to ensure the right "kind" of string is making its way down to `stdout_log_path` and `stderr_log_path`, and possibly removing the `Deref<Target=String>` implementation for `ServiceGroup`, since there is clearly more than one kind of string-like thing we could conceivably want out of a `ServiceGroup`. Such an approach appears to get us rather far afield into the workings and usage of our `outputln!` macros, which is an entirely different can of worms that is best left unopened here (see #6584 for more on that). Signed-off-by: Christopher Maier <cmaier@chef.io>
This PR includes the following key parts:
core
. For a more complete description of that work see moving some common functionality from habitat core#90hab pkg compile
command that will compile all templates in a package in an ad hoc mannerinstall
hook upon loading a package and also adding aInstallHookMode
argument tohab pkg install
that takes one of 3 values:default
,ignore
, andforce
.install
hooks when assembling the build root locally and then forces the hook when executing the docker file.The
InstallHookMode
values have the following corresponding behavior:default
: Runs theinstall
hook when initially loading/installing a packageignore
: Ignores anyinstall
hooks when loading a package (this will be key for exporter scenarios and debugging)
force: will execute the
install` hook of package and any deps regardless of whether they have been previously installed (also key for exporters and debugging)Note: I considered adding arguments to the
compile
sub command for saving the rendered templates in an alternate directory. I'm not strictly opposed to adding that in but the current use cases will need them to be in thehab/svc
directory and adding the ability to save them elsewhere just adds more refactoring that can be added in a separate PR.Signed-off-by: mwrock matt@mattwrock.com