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

Up: teardown when command context is cancelled #11292

Merged
merged 2 commits into from Jan 2, 2024

Conversation

laurazard
Copy link
Member

What I did

Previously, if a long-lived plugin process (such as an execution of compose up) was running and then detached from a terminal, signalling the parent CLI process to exit would leave the plugin process behind.

To address this, changes were introduced on the CLI side (see: docker/cli#4599) to enable the CLI to notify a running plugin process that it should exit. This makes it so that, when the parent CLI process is going to exit, the command context of the plugin command being executed is cancelled.

This PR takes advantage of that change by using that channel on an up to know when to teardown.

Also fixed the "cancellable context" detection in cmd/compose/compose.go, as the string suffix matching was missing contexts such as:

context.Background.WithCancel.WithValue(type trace.traceContextKeyType, val <not Stringer>).WithValue(type api.DryRunKey, val <not Stringer>)

Before:

Screen.Recording.2023-12-20.at.17.40.21.mov

After:

Screen.Recording.2023-12-20.at.17.43.07.mov

Related issue
docker/cli#4599

(not mandatory) A picture of a cute animal, if possible in relation to what you did

Screenshot 2023-12-21 at 12 15 46

@laurazard laurazard requested review from a team, ndeloof, glours and milas and removed request for a team December 21, 2023 12:20
@laurazard laurazard self-assigned this Dec 21, 2023
@laurazard laurazard marked this pull request as draft December 21, 2023 12:35
@laurazard
Copy link
Member Author

Looks like TestComposeCancel/metrics_on_cancel_Compose_build is failing, I'll take a look.

`AdaptCmd` was previously checking for a `.WithCancel` suffix
on context strings, however it's possible for a context to be
cancellable without ending in that suffix, such as when
`context.WithValue` was called after `WithContext`, e.g.:

```go
context.Background.WithCancel.WithValue(type trace.traceContextKeyType,
val <not Stringer>).WithValue(type api.DryRunKey, val <not Stringer>)
```

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
@@ -277,7 +278,12 @@ const PluginName = "compose"

// RunningAsStandalone detects when running as a standalone program
func RunningAsStandalone() bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

Agree! 😅

Copy link

codecov bot commented Dec 22, 2023

Codecov Report

Attention: 3 lines in your changes are missing coverage. Please review.

Comparison is base (17da54d) 56.36% compared to head (dcbf005) 56.22%.
Report is 11 commits behind head on main.

Files Patch % Lines
pkg/compose/up.go 84.21% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #11292      +/-   ##
==========================================
- Coverage   56.36%   56.22%   -0.15%     
==========================================
  Files         134      135       +1     
  Lines       11615    11659      +44     
==========================================
+ Hits         6547     6555       +8     
- Misses       4421     4451      +30     
- Partials      647      653       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Previously, if a long-lived plugin process (such as
an execution of `compose up`) was running and then
detached from a terminal, signalling the parent CLI
process to exit would leave the plugin process behind.

To address this, changes were introduced on the CLI side
(see: docker/cli#4599) to enable
the CLI to notify a running plugin process that it should
exit. This makes it so that, when the parent CLI process
is going to exit, the command context of the plugin
command being executed is cancelled.

This commit takes advantage of these changes by tapping into
the command context's done channel and using it to teardown
on an up.

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
@laurazard laurazard marked this pull request as ready for review December 23, 2023 02:50
Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Member

@milas milas left a comment

Choose a reason for hiding this comment

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

LGTM, and you're my hero for fixing this!

I tested standalone, with Compose CLI trampoline, and with Docker CLI and all looks good. (Well, the standalone case does return exit code 130 but that's not a new regression; I'll open a quick PR for that later.)

@milas milas merged commit 06af729 into docker:main Jan 2, 2024
26 checks passed
laurazard added a commit to laurazard/cli that referenced this pull request Jan 9, 2024
Changes were made in docker#4599 to provide
a mechanism for the CLI to notify running plugin processes that they
should exit, in order to improve the general CLI/plugin UX. The current
implementation boils down to:
1. The CLI creates a socket
2. The CLI executes the plugin
3. The plugin connects to the socket
4. (When) the CLI receives a termination signal, it uses the socket to
   notify the plugin that it should exit
5. The plugin's gets notified via the socket, and cancels it's `cmd.Context`,
   which then gets handled appropriately

This change works in most cases and fixes the issue it sets out to solve
(see: docker/compose#11292) however, in the case
where the user has a TTY attached and the plugin is not already handling
received signals, steps 4+ changes:
4. (When) the CLI receives a termination signal, before it can use the
   socket to notify the plugin that it should exit, the plugin process
   also receives a signal due to sharing the pgid with the CLI

Since we now have a proper "job control" mechanism, we can simplify the
scenarios by executing the plugins with their own process group id,
thereby removing the "double notification" issue and making it so that
plugins can handle the same whether attached to a TTY or not.

In order to make this change "plugin-binary" backwards-compatible, in
the case that a plugin does not connect to the socket, the CLI passes
the signal to the plugin process.

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
laurazard added a commit to laurazard/cli that referenced this pull request Jan 10, 2024
Changes were made in docker#4599 to provide
a mechanism for the CLI to notify running plugin processes that they
should exit, in order to improve the general CLI/plugin UX. The current
implementation boils down to:
1. The CLI creates a socket
2. The CLI executes the plugin
3. The plugin connects to the socket
4. (When) the CLI receives a termination signal, it uses the socket to
   notify the plugin that it should exit
5. The plugin's gets notified via the socket, and cancels it's `cmd.Context`,
   which then gets handled appropriately

This change works in most cases and fixes the issue it sets out to solve
(see: docker/compose#11292) however, in the case
where the user has a TTY attached and the plugin is not already handling
received signals, steps 4+ changes:
4. (When) the CLI receives a termination signal, before it can use the
   socket to notify the plugin that it should exit, the plugin process
   also receives a signal due to sharing the pgid with the CLI

Since we now have a proper "job control" mechanism, we can simplify the
scenarios by executing the plugins with their own process group id,
thereby removing the "double notification" issue and making it so that
plugins can handle the same whether attached to a TTY or not.

In order to make this change "plugin-binary" backwards-compatible, in
the case that a plugin does not connect to the socket, the CLI passes
the signal to the plugin process.

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
laurazard added a commit to laurazard/cli that referenced this pull request Jan 10, 2024
Changes were made in docker#4599 to provide
a mechanism for the CLI to notify running plugin processes that they
should exit, in order to improve the general CLI/plugin UX. The current
implementation boils down to:
1. The CLI creates a socket
2. The CLI executes the plugin
3. The plugin connects to the socket
4. (When) the CLI receives a termination signal, it uses the socket to
   notify the plugin that it should exit
5. The plugin's gets notified via the socket, and cancels it's `cmd.Context`,
   which then gets handled appropriately

This change works in most cases and fixes the issue it sets out to solve
(see: docker/compose#11292) however, in the case
where the user has a TTY attached and the plugin is not already handling
received signals, steps 4+ changes:
4. (When) the CLI receives a termination signal, before it can use the
   socket to notify the plugin that it should exit, the plugin process
   also receives a signal due to sharing the pgid with the CLI

Since we now have a proper "job control" mechanism, we can simplify the
scenarios by executing the plugins with their own process group id,
thereby removing the "double notification" issue and making it so that
plugins can handle the same whether attached to a TTY or not.

In order to make this change "plugin-binary" backwards-compatible, in
the case that a plugin does not connect to the socket, the CLI passes
the signal to the plugin process.

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
neersighted added a commit to laurazard/cli that referenced this pull request Jan 12, 2024
Changes were made in 1554ac3 to provide
a mechanism for the CLI to notify running plugin processes that they
should exit, in order to improve the general CLI/plugin UX. The current
implementation boils down to:
1. The CLI creates a socket
2. The CLI executes the plugin
3. The plugin connects to the socket
4. (When) the CLI receives a termination signal, it uses the socket to
   notify the plugin that it should exit
5. The plugin's gets notified via the socket, and cancels it's `cmd.Context`,
   which then gets handled appropriately

This change works in most cases and fixes the issue it sets out to solve
(see: docker/compose#11292) however, in the case
where the user has a TTY attached and the plugin is not already handling
received signals, steps 4+ changes:
4. (When) the CLI receives a termination signal, before it can use the
   socket to notify the plugin that it should exit, the plugin process
   also receives a signal due to sharing the pgid with the CLI

Since we now have a proper "job control" mechanism, we can simplify the
scenarios by executing the plugins with their own process group id,
thereby removing the "double notification" issue and making it so that
plugins can handle the same whether attached to a TTY or not.

In order to make this change "plugin-binary" backwards-compatible, in
the case that a plugin does not connect to the socket, the CLI passes
the signal to the plugin process.

Co-authored-by: Bjorn Neergaard <bjorn.neergaard@docker.com>
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
Signed-off-by: Bjorn Neergaard <bjorn.neergaard@docker.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants