Skip to content
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

Store configurable shutdown parameters #6539

Merged
merged 21 commits into from Jun 12, 2019

Conversation

@davidMcneil
Copy link
Contributor

commented May 9, 2019

This is the initial work to finish the configurable shutdown feature started here #6450. There are three levels of precedence for the configurable shutdown timeout:
1 svc stop or svc unload
2. svc load
3. plan.sh.

The configurable shutdown signal can only be set in the plan.sh.

TODO

  • Add pkg_shutdown_timeout and pkg_shutdown_signal to the default template. Waiting for #6495 to merge
  • Add windows build support to hab-plan-build.ps1
  • Test windows support
  • Resolve stop issue discussed here

Resolves #6451

@chef-expeditor

This comment has been minimized.

Copy link

commented May 9, 2019

Hello davidMcneil! Thanks for the pull request!

Here is what will happen next:

  1. Your PR will be reviewed by the maintainers.
  2. If everything looks good, one of them will approve it, and your PR will be merged.

Thank you for contributing!

@christophermaier
Copy link
Contributor

left a comment

@davidMcneil Looks great so far! I think there are some other cleanups we can squeeze out of this, but I'm really liking where it's going!

.clone()
.expect("No package release in PackageInstall"),
shutdown_signal: package.shutdown_signal()?.unwrap_or_else(Default::default),
shutdown_timeout: package.shutdown_timeout()?.unwrap_or_else(Default::default) };

This comment has been minimized.

Copy link
@christophermaier

christophermaier May 10, 2019

Contributor

These could use unwrap_or_default() instead of unwrap_or_else(Default::default)

This comment has been minimized.

Copy link
@baumanj

baumanj May 13, 2019

Member

It strikes me as very awkward that PackageInstall::shutdown_signal and ::shutdown_timeout can fail. Delegating the error handling of read_metafile to the callers of the individual accessors seems like the wrong pattern here. Returning Option<ShutdownSignal> is fine, but passing around a PackageInstall which may fail in read_metafile seems like forcing the error handling later than we want. I'd expect the failures to occur when PackageInstall is created. I understand the impetus to only call read_metafile lazily, but I don't think it's necessary for performance and makes the code more awkward and harder to reason about.

I know this predates your change and would require some reworking to address, but I think it's worthwhile to avoid perpetuating a bad pattern.

This comment has been minimized.

Copy link
@christophermaier

christophermaier May 15, 2019

Contributor

I completely agree about pushing error handling down into PackageInstall, but I think that is going to be a piece of work by itself, and may have far-reaching impact. I don't think that should be a part of this PR.

This comment has been minimized.

Copy link
@baumanj

baumanj May 15, 2019

Member

Can we get an issue filed and do it as a direct follow up? If not, I think the likelihood of this getting fixed is low.

This comment has been minimized.

Copy link
@christophermaier

christophermaier May 15, 2019

Contributor

@baumanj We can file an issue, but how direct a follow-up it would be will need some research. I started to look into this a while ago, but I think it had a much bigger footprint that you might otherwise think.

This comment has been minimized.

Copy link
@davidMcneil

davidMcneil May 17, 2019

Author Contributor

Created issue #6572.

app
}
/// a customized timeout.
fn add_configurable_shutdown_options(mut app: App<'static, 'static>) -> App<'static, 'static> {

This comment has been minimized.

Copy link
@christophermaier

christophermaier May 10, 2019

Contributor

Since this function is just dealing with a single option now, we might want to change the name of this function to something like add_shutdown_timeout_option.

a shutdown signal to wait before \
killing a service process")
.long("shutdown-timeout")
.takes_value(true));

This comment has been minimized.

Copy link
@christophermaier

christophermaier May 10, 2019

Contributor

Also, since we're just adding a single option, you can get rid of the mutability here:

fn add_configurable_shutdown_options(app: App<'static, 'static>) -> App<'static, 'static> {
    app.arg(Arg::with_name("SHUTDOWN_TIMEOUT").help("The number of seconds after sending a \
                                                     shutdown signal to wait before killing a \
                                                     service process")
                                              .long("shutdown-timeout")
                                              .takes_value(true))
}
@@ -1051,6 +1050,8 @@ fn sub_svc_load(m: &ArgMatches<'_>) -> Result<()> {
update_svc_load_from_input(m, &mut msg)?;

This comment has been minimized.

Copy link
@christophermaier

christophermaier May 10, 2019

Contributor

This reminds me... we'll also need to add this option to the run command, as well.

(hab sup run takes options to configure the running of the Supervisor, but it also can take the same options that hab svc load does. Normally, you would just start up a Supervisor using hab sup run without any of the service-specific options. However, for scenarios like containers it is convenient to be able to start the container and configure the (single) service it is going to run in a single invocation.)

Looking at update_svc_load_from_input, it always takes an unmodified default SvcLoad struct as input, so we could just create that struct inside that function (and remove the update_ prefix from its name). We'd also want to add the shutdown timeout parsing call to that function.

.map(|s| s.parse().map_err(Into::into))
// Convert from Option<Result<_>> to Result<Option<_>>
.map_or(Ok(None), |o| o.map(Some))
}

This comment has been minimized.

Copy link
@christophermaier

christophermaier May 10, 2019

Contributor

This is pretty slick 👍

This comment has been minimized.

Copy link
@baumanj

baumanj May 13, 2019

Member

This is very slick indeed.

From a usability and code simplicity perspective, I think we're better off putting the validation into CLAP (with validator, see example) so that the user has a consistent experience and then we don't need to deal with the potential failures at this level.

This comment has been minimized.

Copy link
@christophermaier

christophermaier May 15, 2019

Contributor

Man, Github's UI is being very weird today...

I have a thought: #6539 (comment)

}

// Request to unload a loaded service.
message SvcUnload {
optional sup.types.PackageIdent ident = 1;
// Name of the signal to send the service to shut it down (e.g.,
// "TERM" and not "SIGTERM"). Only applies to Unix platforms.
optional string signal = 2;
// Timeout in before killing the service
optional uint32 timeout_in_seconds = 3;
}

This comment has been minimized.

Copy link
@christophermaier

christophermaier May 10, 2019

Contributor

When removing a field from a protobuf message, it's important to prevent both the field name and field number from being reused in the future, which can break compatibility guarantees.

Check out the docs for more.

@@ -119,9 +118,6 @@ message SvcStart {
// Request to stop a loaded and started service.
message SvcStop {
optional sup.types.PackageIdent ident = 1;
// Name of the signal to send the service to shut it down (e.g.,
// "TERM" and not "SIGTERM"). Only applies to Unix platforms.
optional string signal = 2;

This comment has been minimized.

Copy link
@christophermaier

christophermaier May 10, 2019

Contributor

Same as above.

@@ -86,7 +88,7 @@ impl From<ShutdownTimeout> for Duration {
// but we are making it available on Windows as well for situations
// where a Windows CLI is communicating with a Linux Supervisor.
#[allow(non_snake_case)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Deserialize, Serialize, Eq, PartialEq, Debug, Clone, Copy, Hash)]

This comment has been minimized.

Copy link
@christophermaier

christophermaier May 10, 2019

Contributor

Similar logic applies here as for the ShutdownSignal above, but with a twist.

The reason you need to have Serialize and Deserialize here at all is that ServiceStatus has derived Deserialize.

#[derive(Deserialize)]
struct ServiceStatus {
pkg: Pkg,
process: ProcessStatus,
service_group: ServiceGroup,
desired_state: DesiredState,
}

This struct also has a pkg field, even though it ultimately only really needs the ident field from Pkg.

Because of all this, Pkg needs to implement Deserialize, which then cascades out to ShutdownSignal and ShutdownTimeout. If we could rework ServiceStatus to not need Pkg, then that cuts off the chain, meaning we don't need to implement either Serialize or Deserialize for the shutdown types.

(Actually, Service and Pkg don't really even need to implement Serialize at all anymore, so we can go ahead and get rid of a bunch of Serialize implementations right now!)

So, in the end, I think we can remove those Serialize impls, and temporarily derive Deserialize for the ShutdownTimeout. Once we can rework ServiceStatus, we can then remove that, too.

(To be clear, ShutdownTimeout will need both Serialize and Deserialize because the spec file logic depends on it.)

(The reason that Service and Pkg implement Serialize is actually a leftover of history, which you can catch up on in #4823. The TL;DR of it is that the data that actually gets serialized for templating is not Pkg anymore, but rather a proxy object that allows us to separate the internal implementation details from the interface we present to users. There was a similar change that was made for Service later on, in #5689.

I don't think there's currently a really compelling reason to add the signal and shutdown data to that PkgProxy right now (I'm not quite sure what you'd meaningfully want to do with them in a template). If people ask for it, we can easily add it later.

@@ -51,7 +53,7 @@ use time::Duration;
/// throughout our code, which can be confusing, we can just pass this
/// around, and turn it into a `time::Duration` at the last possible
/// moment.)
#[derive(Debug, Clone)]
#[derive(Deserialize, Serialize, Eq, PartialEq, Debug, Clone, Hash)]
pub struct ShutdownTimeout(u32);

This comment has been minimized.

Copy link
@christophermaier

christophermaier May 10, 2019

Contributor

This reminded me of #6469.

Though deriving Serialize and Deserialize currently does the "right thing" here (we'll end up getting the same thing we'd get if we were to go through the FromStr and Display traits), if we were to end up changing the underlying implementation details of Signal, we could potentially introduce incompatibilities elsewhere, particularly in spec file serialization.

I don't foresee us changing the underlying u32 here, but Duration is also a logical choice, and it serializes very differently. Locking down the concrete behavior we want here is the safe choice to make.

@baumanj
Copy link
Member

left a comment

Functionality all looks good to me!

I think we can simplify some things, but all these comments are yours to take or leave as you see fit.

I would like to see the one TODO addressed before merging, but I think @christophermaier has more context there.

.clone()
.expect("No package release in PackageInstall"),
shutdown_signal: package.shutdown_signal()?.unwrap_or_else(Default::default),
shutdown_timeout: package.shutdown_timeout()?.unwrap_or_else(Default::default) };

This comment has been minimized.

Copy link
@baumanj

baumanj May 13, 2019

Member

It strikes me as very awkward that PackageInstall::shutdown_signal and ::shutdown_timeout can fail. Delegating the error handling of read_metafile to the callers of the individual accessors seems like the wrong pattern here. Returning Option<ShutdownSignal> is fine, but passing around a PackageInstall which may fail in read_metafile seems like forcing the error handling later than we want. I'd expect the failures to occur when PackageInstall is created. I understand the impetus to only call read_metafile lazily, but I don't think it's necessary for performance and makes the code more awkward and harder to reason about.

I know this predates your change and would require some reworking to address, but I think it's worthwhile to avoid perpetuating a bad pattern.

.map(|s| s.parse().map_err(Into::into))
// Convert from Option<Result<_>> to Result<Option<_>>
.map_or(Ok(None), |o| o.map(Some))
}

This comment has been minimized.

Copy link
@baumanj

baumanj May 13, 2019

Member

This is very slick indeed.

From a usability and code simplicity perspective, I think we're better off putting the validation into CLAP (with validator, see example) so that the user has a consistent experience and then we don't need to deal with the potential failures at this level.

@@ -244,6 +244,22 @@ _render_metadata_SVC_USER() {
echo "$pkg_svc_user" > "$pkg_prefix"/SVC_USER
}

_render_metadata_SHUTDOWN_SIGNAL() {
if [[ -n "$pkg_shutdown_signal" ]]; then

This comment has been minimized.

Copy link
@baumanj

baumanj May 13, 2019

Member

Since this file gets sourced by others, so we don't know whether or not set -u, which aborts on unbound variable expansion will be in effect. In that case, it's best to program defensively and handle the unset situation, by replacing it with the empty string (see the Parameter Expansion docs; definitely worth bookmarking):

Suggested change
if [[ -n "$pkg_shutdown_signal" ]]; then
if [[ -n "${pkg_shutdown_signal:-}" ]]; then

Similarly elsewhere

Also, there's a fair bit of redundancy here. We could add

_render_metadata() {
  local key=${1?required argument: metadata key}
  local value=${2?required argument: metadata value}
  local required=${3:-false}
  if "$required" || [[ -n "${value:-}" ]]; then
    debug "Rendering $key metadata file"
    # shellcheck disable=2154
    echo "$value" > "$pkg_prefix"/"$key"
  fi
}

and then the callers change from

_render_metadata_SVC_GROUP
_render_metadata_SVC_USER
_render_metadata_SHUTDOWN_SIGNAL
_render_metadata_SHUTDOWN_TIMEOUT

to

_render_metadata SVC_GROUP "$pkg_svc_group" true
_render_metadata SVC_USER "$pkg_svc_user" true
_render_metadata SHUTDOWN_SIGNAL "$pkg_shutdown_signal"
_render_metadata SHUTDOWN_TIMEOUT "$pkg_shutdown_timeout"

or if we add a helper:

_render_required_metadata() {
  _render_metadata "$@" true
}
_render_required_metadata SVC_GROUP "$pkg_svc_group"
_render_required_metadata SVC_USER "$pkg_svc_user"
_render_metadata SHUTDOWN_SIGNAL "$pkg_shutdown_signal"
_render_metadata SHUTDOWN_TIMEOUT "$pkg_shutdown_timeout"

This comment has been minimized.

Copy link
@davidMcneil

davidMcneil May 17, 2019

Author Contributor

I would prefer to do the redundancy cleanup as part of another PR. I opened up ticket #6571.

#[derive(Clone, Debug, Default)]
pub struct ShutdownSpec {
pub struct ShutdownInput {

This comment has been minimized.

Copy link
@baumanj

baumanj May 13, 2019

Member

Since this just takes a u32 wrapper and puts it in an option, I think it should be Copy. For now this simplifies consuming code. If this struct grows large enough later that it doesn't make sense, the compiler will guide us towards the appropriate changes elsewhere.

@@ -51,7 +53,7 @@ use time::Duration;
/// throughout our code, which can be confusing, we can just pass this
/// around, and turn it into a `time::Duration` at the last possible
/// moment.)
#[derive(Debug, Clone)]
#[derive(Deserialize, Serialize, Eq, PartialEq, Debug, Clone, Hash)]

This comment has been minimized.

Copy link
@baumanj

baumanj May 13, 2019

Member
Suggested change
#[derive(Deserialize, Serialize, Eq, PartialEq, Debug, Clone, Hash)]
#[derive(Deserialize, Serialize, Eq, PartialEq, Debug, Clone, Copy, Hash)]

Structs which wrap Copy typesshould themselves beCopy` since they're representationally identical to the underlying type and it simplifies consuming code.

ShutdownSpec { signal, timeout }
impl Into<ShutdownInput> for habitat_sup_protocol::ctl::SvcUnload {
fn into(self) -> ShutdownInput {
ShutdownInput { timeout: self.timeout_in_seconds.map(Into::into), }

This comment has been minimized.

Copy link
@baumanj

baumanj May 13, 2019

Member

In instances where we know the type, and not just the trait bound, I prefer to use it since I think it makes the code clearer. I'd prefer:

Suggested change
ShutdownInput { timeout: self.timeout_in_seconds.map(Into::into), }
ShutdownInput { timeout: self.timeout_in_seconds.map(ShutdownTimeout::from), }

but this is also preferable to Into::into in my opinion:

Suggested change
ShutdownInput { timeout: self.timeout_in_seconds.map(Into::into), }
ShutdownInput { timeout: self.timeout_in_seconds.map(u32::into), }

Similarly elsewhere

This comment has been minimized.

Copy link
@davidMcneil

davidMcneil May 16, 2019

Author Contributor

I noticed this previous comment. I do not have a particularly strong preferences either way, but I would like to standardize on a common method.

This comment has been minimized.

Copy link
@baumanj

baumanj May 20, 2019

Member

I agree that a standard is best; no matter what it is. Let's continue the discussion over at that aforementioned comment.

@@ -151,7 +153,7 @@ impl fmt::Display for Signal {
/// Encapsulates logic for defining the default shutdown signal we
/// send services, and handles translation from external types at the
/// edges of our system.
#[derive(Debug, Clone)]
#[derive(Deserialize, Serialize, Eq, PartialEq, Debug, Clone, Hash)]

This comment has been minimized.

Copy link
@baumanj

baumanj May 13, 2019

Member
Suggested change
#[derive(Deserialize, Serialize, Eq, PartialEq, Debug, Clone, Hash)]
#[derive(Deserialize, Serialize, Eq, PartialEq, Debug, Clone, Copy, Hash)]
pub struct ShutdownConfig {
pub signal: ShutdownSignal,
pub timeout: ShutdownTimeout,
}

This comment has been minimized.

Copy link
@baumanj

baumanj May 13, 2019

Member

I think these can be merged into one structure declaration which makes the difference clearer and eliminates some redundancy

pub struct ShutdownConfig {
    #[cfg(unix)]
    pub signal: ShutdownSignal,
    pub timeout: ShutdownTimeout,
}
})
});
let signal = pkg.shutdown_signal.clone();
Self { signal, timeout }

This comment has been minimized.

Copy link
@baumanj

baumanj May 13, 2019

Member

Like the declaration, instead of fully separate unix and windows impl blocks, I think this puts a focus on the differences:

        #[cfg(unix)]
        let shutdown_config = Self { signal: pkg.shutdown_signal.clone(),
                                     timeout };

        #[cfg(windows)]
        let shutdown_config = Self { timeout };

        shutdown_config
@@ -149,6 +150,7 @@ impl IntoServiceSpec for habitat_sup_protocol::ctl::SvcLoad {
if let Some(ref interval) = self.health_check_interval {
spec.health_check_interval = interval.seconds.into()
}
spec.shutdown_timeout = self.shutdown_timeout.map(Into::into);

This comment has been minimized.

Copy link
@baumanj

baumanj May 13, 2019

Member

Same comment as previously about Into::into

@davidMcneil davidMcneil force-pushed the dmcneil/configurable-shutdown branch from 8fc1460 to 5ccd81a May 15, 2019

#[derive(Debug, Clone)]
pub struct ShutdownSignal(Signal);
#[derive(Deserialize, Debug, Clone, Copy)]
pub struct ShutdownSignal(#[serde(with = "serde_string")] Signal);

This comment has been minimized.

Copy link
@christophermaier

christophermaier May 15, 2019

Contributor

This is cool, but after doing some digging, I see that we could also do something like this, using the from attribute:

#[derive(Deserialize, Debug, Clone, Copy)]
#[serde(from = "Signal")]
pub struct ShutdownSignal(Signal);

Using serde_string is good, too, so I bring it up mainly as a way of sharing something nifty I just uncovered 😄 As we get a bit more principled with how we're using Serde, though, I think it would be good to dig further into the crate to try and uncover ways like this to leverage existing code.

(I went on a search for other alternatives, because I find it a little amazing that there doesn't seem to be support built into Serde for automatically taking advantage of existing Display and FromStr implementations on a type to do serialization and deserialization.)

I really do like how you refactored the serde_string stuff to make it more ergonomic to use, though; that's a nice touch!

This comment has been minimized.

Copy link
@davidMcneil

davidMcneil May 15, 2019

Author Contributor

I had not seen "from" that is cool. I found this open issue about adding documentation for the method I used. Which makes me think that maybe it is their preferred method? There serialize and deserialize implementations are simpler than ours so we could maybe clean it up more.

This comment has been minimized.

Copy link
@christophermaier

christophermaier May 15, 2019

Contributor

Good find! Yeah, that implementation looks nice (especially with the right trait bounds).

I like it 👍

@@ -1095,15 +1095,25 @@ pub fn sub_svc_status() -> App<'static, 'static> {
)
}

fn sub_svc_stop(feature_flags: FeatureFlag) -> App<'static, 'static> {
pub fn parse_optional_arg<T: FromStr, E>(name: &str, m: &ArgMatches) -> Result<Option<T>, E>

This comment has been minimized.

Copy link
@christophermaier

christophermaier May 15, 2019

Contributor

I think we will only be using this in the case that we've successfully gotten past the validation stage.

In that case, we could tighten up the return type to be simply Option<T>, since presumably any input that couldn't be successfully parsed would have been weeded out before now. We could tack an expect() to s.parse(), and call it a day.

(I've been writing some code elsewhere that deals with these conversions and it's been both clarifying and simplifying to write functions with the knowledge that the input will have been validated beforehand.)

@christophermaier

This comment has been minimized.

Copy link
Contributor

commented May 15, 2019

@davidMcneil This all looks really good. I'm gonna have a think about how best to tackle that outstanding TODO.

@davidMcneil davidMcneil force-pushed the dmcneil/configurable-shutdown branch from 5ccd81a to 395aa27 May 16, 2019

@davidMcneil davidMcneil force-pushed the dmcneil/configurable-shutdown branch from cb7322f to 4f162f0 May 17, 2019

@davidMcneil davidMcneil force-pushed the dmcneil/configurable-shutdown branch 2 times, most recently from c57c431 to e541178 May 28, 2019

@christophermaier
Copy link
Contributor

left a comment

@davidMcneil I tried this out and it works like a charm. Nice work!

I do have a small suggestion, though... let me know what you think.

}

_render_metadata_SHUTDOWN_TIMEOUT() {
if [[ -n "${pkg_shutdown_timeout:-}" ]]; then

This comment has been minimized.

Copy link
@christophermaier

christophermaier May 28, 2019

Contributor

What do you think about having the variable be named pkg_shutdown_timeout_sec, to convey the units?

@christophermaier

This comment has been minimized.

Copy link
Contributor

commented May 28, 2019

@christophermaier

This comment has been minimized.

Copy link
Contributor

commented May 31, 2019

@davidMcneil Looks great; feel free to merge whenever you like!

@davidMcneil davidMcneil force-pushed the dmcneil/configurable-shutdown branch 2 times, most recently from 00debf3 to e5b4bbc Jun 6, 2019

@baumanj
Copy link
Member

left a comment

Address these small issues in www and then I think we should be good to go. Since @raskchanky is the only www code owner, you'll need to do an admin merge.

: Optional. The signal to send the service to shutdown. The default is `TERM`.

```bash
pkg_shutdown_signal=$pkg_shutdown_signal

This comment has been minimized.

Copy link
@baumanj

baumanj Jun 11, 2019

Member

This example could be more helpful. Why not something like

Suggested change
pkg_shutdown_signal=$pkg_shutdown_signal
pkg_shutdown_signal=HUP

?

**Optional**. The signal to send the service to shutdown. The default is `TERM`.

```bash
pkg_shutdown_signal=$pkg_shutdown_signal

This comment has been minimized.

Copy link
@baumanj

baumanj Jun 11, 2019

Member

Same as above

@davidMcneil davidMcneil force-pushed the dmcneil/configurable-shutdown branch from ec04702 to 5ce2da5 Jun 11, 2019

davidMcneil added some commits May 9, 2019

Store configurable shutdown parameters
There are three places that a configurable shutdown timeout
may be set: 1 `svc stop` or `svc unload` 2. `svc load`
3. `plan.sh`. The configurable shutdown signal can only be
set in the `plan.sh`.

Signed-off-by: David McNeil <dmcneil@chef.io>
Use unwrap_or_default
Signed-off-by: David McNeil <dmcneil@chef.io>

davidMcneil added some commits May 13, 2019

Rename to add_shutdown_timeout_option
Signed-off-by: David McNeil <dmcneil@chef.io>
Add --shutdown-timeout to hab sup run
Signed-off-by: David McNeil <dmcneil@chef.io>
Cleanup serialization
Remove unnecessary deriving from Serialize
Use string methods for serialization where
it makes sense

Signed-off-by: David McNeil <dmcneil@chef.io>
Add copy to shutdown timeout and signal
Signed-off-by: David McNeil <dmcneil@chef.io>
Use stored shutdown config on noninteractive shutdown
Signed-off-by: David McNeil <dmcneil@chef.io>
Add shutdown signal Serialize to fix status
Signed-off-by: David McNeil <dmcneil@chef.io>
Reduce duplication of conditional compilation
Signed-off-by: David McNeil <dmcneil@chef.io>
Use validator for --shutdown-timeout
Signed-off-by: David McNeil <dmcneil@chef.io>
Add shutdown config to plan.sh template
Signed-off-by: David McNeil <dmcneil@chef.io>
Convert Into::into into from
Signed-off-by: David McNeil <mcneil.david2@gmail.com>
Add powershell support for shutdown timeout in plan
Signed-off-by: David McNeil <dmcneil@chef.io>
Add documentation for --shutdown-timeout
Signed-off-by: David McNeil <dmcneil@chef.io>
Fix build on windows
Signed-off-by: David McNeil <dmcneil@chef.io>
Remove copyright
Signed-off-by: David McNeil <dmcneil@chef.io>
Rename pkg_shutdown_timeout to pkg_shutdown_timeout_sec
Signed-off-by: David McNeil <dmcneil@chef.io>
Add shutdown ducumentation
Signed-off-by: David McNeil <dmcneil@chef.io>
Fix testcase
Signed-off-by: David McNeil <mcneil.david2@gmail.com>
Fix shutdown_timeout serialization
Signed-off-by: David McNeil <mcneil.david2@gmail.com>
Add example signal to docs
Signed-off-by: David McNeil <mcneil.david2@gmail.com>

@davidMcneil davidMcneil force-pushed the dmcneil/configurable-shutdown branch from 5ce2da5 to 0abbeb0 Jun 12, 2019

@davidMcneil davidMcneil merged commit b5716da into master Jun 12, 2019

5 checks passed

DCO This commit has a DCO Signed-off-by
Details
buildkite/habitat-sh-habitat-master-verify Build #2296 passed (46 minutes, 28 seconds)
Details
buildkite/habitat-sh-habitat-master-website Build #2610 passed (35 seconds)
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
expeditor/config-validation Validated your Expeditor config file
Details

@davidMcneil davidMcneil deleted the dmcneil/configurable-shutdown branch Jun 12, 2019

chef-ci added a commit that referenced this pull request Jun 12, 2019

Update CHANGELOG.md with details from pull request #6539
Obvious fix; these changes are the result of automation not creative thinking.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.