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

signal: custom exit code status for interceptor #5636

Closed

Conversation

DarthBenro008
Copy link

Outline and Challenge

This issue was required lnd to exit with status code 1 where there was an request to shutdownRequestChannel

lnd/signal/signal.go

Lines 100 to 102 in 52767a7

case <-c.shutdownRequestChannel:
log.Infof("Received shutdown request.")
shutdown()

The challenge was that we had to do an os.exit(1) after an graceful shutdown.

An graceful shutdown occurs when all the deferred go function in the Main() function clean up, but there is no way that we can pass the data or tell the function that cleans up last to exit with a particular status code.

Approach

I pulled the defer function of Main() outside from lnd package and put it under cmd/lnd.go where the Main() get's called (giving us parental access to shutdownInterceptor) and added an additional parameter to Interceptor struct so that we can pass data when an graceful shutdown has occurred.

I am passing a pointer reference in-order to retain the modifications (setting exit code) in the Intercept struct in goroutines.

Before After
lnd_before lnd_after

Steps to test this PR

  • run bitcoind server
  • run the following ./lnd-debug --rpclisten=localhost:10001 --listen=localhost:10011 --restlisten=localhost:8001 --debuglevel=debug --healthcheck.chainbackend.backoff=1s --healthcheck.chainbackend.interval=1m0s --healthcheck.chainbackend.timeout=3s
  • create a wallet ./lncli-debug --rpcserver=localhost:10001 --macaroonpath=data/chain/bitcoin/simnet/admin.macaroon unlock
  • Press ctrl+c and shutdown your bitcoind server
  • Wait for 1 min, lnd will exit gracefully with an exit status code 1

Pull Request Checklist

  • All changes are Go version 1.15 compliant
  • Your PR passes all CI checks. If a check cannot be passed for a justifiable reason, that reason must be stated in the commit message and PR description.
  • If this is your first time contributing, we recommend you read the Code Contribution Guidelines
  • For new code: Code is accompanied by tests which exercise both the positive and negative (error paths) conditions (if applicable)
  • For bug fixes: If possible, code is accompanied by new tests which trigger the bug being fixed to prevent regressions
  • Any new logging statements use an appropriate subsystem and logging level
  • For code and documentation: lines are wrapped at 80 characters (the tab character should be counted as 8 characters, not 4, as some IDEs do per default)
  • A description of your changes should be added to running the release notes for the milestone your change will land in.

@DarthBenro008
Copy link
Author

DarthBenro008 commented Aug 17, 2021

Hi @carlaKC ! I have solved the issue, ensured the CI checks pass with a PR on personal fork 😃

@Roasbeef Roasbeef added this to the v0.14.0 milestone Aug 17, 2021
cmd/lnd/main.go Outdated Show resolved Hide resolved
@DarthBenro008 DarthBenro008 requested a review from Roasbeef August 18, 2021 02:49
Copy link
Collaborator

@carlaKC carlaKC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! Looking good

cmd/lnd/main.go Outdated Show resolved Hide resolved
signal/signal.go Outdated Show resolved Hide resolved
signal/signal.go Show resolved Hide resolved
cmd/lnd/main.go Outdated Show resolved Hide resolved
@DarthBenro008
Copy link
Author

Hey @carlaKC Implemented and amended the suggested changes!

@DarthBenro008
Copy link
Author

Force pushed from 1057f6d to 1764854 to fix a lint issue reported by Travis.

Copy link
Collaborator

@carlaKC carlaKC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the quick turnaround!

Two things:

  1. Why not just pass a pointer to the Interceptor around?
  2. If we're going to use a channel for the exit code, it needs to always have a value sent in so that the caller can read the exit code value without getting blocked.

signal/signal.go Outdated Show resolved Hide resolved
signal/signal.go Show resolved Hide resolved
@@ -75,7 +75,7 @@ func Start(extraArgs string, rpcReady Callback) {

// Load the configuration, and parse the extra arguments as command
// line options. This function will also set up logging properly.
loadedConfig, err := lnd.LoadConfig(shutdownInterceptor)
loadedConfig, err := lnd.LoadConfig(*shutdownInterceptor)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we want to dereference here? Can't we just pass a pointer in?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially tried passing it directly to figure out the LoadConfig does not accpet and reference to Interceptor. I was not sure which all aspects of LND would be using the loadConfig as it seems very important to parse the user configs, hence i decided to go the safe way and passed a de-referenced interceptor

lnd/config.go

Line 569 in 9e8b9cc

func LoadConfig(interceptor signal.Interceptor) (*Config, error) {

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can bump LoadConfig to accept a pointer 👍

This commit adds an ability to interceptor to have custom exit status-codes for interceptor that allows to exit after an graceful exit

Signed-off-by: DarthBenro008 <[email protected]>
@DarthBenro008
Copy link
Author

Hey @carlaKC , I have implemented the recommended changes and have resolved the conversations 😄

@DarthBenro008 DarthBenro008 requested a review from carlaKC August 20, 2021 14:34
Copy link
Collaborator

@carlaKC carlaKC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shaping up nicely!

if !atomic.CompareAndSwapInt32(&started, 0, 1) {
return Interceptor{}, errors.New("intercept already started")
return &Interceptor{}, errors.New("intercept already started")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can just return nil here, safe to assume that it won't be used when there's a non-nil error :)

@@ -34,9 +34,10 @@ func main() {
// Call the "real" main in a nested manner so the defers will properly
// be executed in the case of a graceful shutdown.
if err = lnd.Main(
loadedConfig, lnd.ListenerCfg{}, shutdownInterceptor,
loadedConfig, lnd.ListenerCfg{}, *shutdownInterceptor,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still dereferencing here?

@@ -34,20 +34,24 @@ type Interceptor struct {

// quit is closed when instructing the main interrupt handler to exit.
quit chan struct{}

// exitCode is the exit code status when the main interrupt handler exits
exitCode chan int
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth adding a comment here that we must set a value here, otherwise the GetExitCode function will block.

shutdown()

case <-c.quit:
log.Infof("Gracefully shutting down.")
close(c.shutdownChannel)
signal.Stop(c.interruptChannel)
// Adding exit status code 0 as this will quit after graceful shutdown
c.setExitCode(0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to set an exit code here? I think we can only hit this case if we've called shutdown in one of the other cases?

We can't send multiple values into the channel otherwise we'll end up blocked. I think it's worth adding a comment above <-c.quit that we only reach this case once shutdown has been called.

@@ -75,7 +75,7 @@ func Start(extraArgs string, rpcReady Callback) {

// Load the configuration, and parse the extra arguments as command
// line options. This function will also set up logging properly.
loadedConfig, err := lnd.LoadConfig(shutdownInterceptor)
loadedConfig, err := lnd.LoadConfig(*shutdownInterceptor)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can bump LoadConfig to accept a pointer 👍

@Roasbeef Roasbeef added the P3 might get fixed, nice to have label Aug 31, 2021
@Roasbeef Roasbeef modified the milestones: v0.14.0, v0.15.0 Sep 29, 2021
@Roasbeef Roasbeef removed their request for review September 29, 2021 03:46
@carlaKC
Copy link
Collaborator

carlaKC commented Nov 12, 2021

@DarthBenro008 any updates here?

@lightninglabs-deploy
Copy link

@DarthBenro008, remember to re-request review from reviewers when ready

@carlaKC
Copy link
Collaborator

carlaKC commented Jan 3, 2022

Closing due to inactivity.

@mohamedawnallah
Copy link
Contributor

Replaced by #8659

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
error codes linux P3 might get fixed, nice to have
Projects
None yet
Development

Successfully merging this pull request may close these issues.

lnd should exit with no-success error code when automatically shut down due to bitcoind problems
5 participants