-
Notifications
You must be signed in to change notification settings - Fork 96
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
Maintenance locks and interactivity #598
Maintenance locks and interactivity #598
Conversation
@@ -1678,6 +1657,9 @@ static const char *get_frequency(enum schedule_priority schedule) | |||
} | |||
} | |||
|
|||
static const char *custom_background_config = | |||
"-c credential.interactive=false -c cred.askPass=false"; | |||
|
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.
Just a nit. As an alternative to defining a static string here, you could #define a macro and use that in each of the fprintf() format strings below (like we do for the PRIuMAX
or PRItime
strings).
credential.c
Outdated
|
||
if (!git_config_get_int("credential.timeout", &config_seconds) && | ||
config_seconds >= 0) | ||
select_timeout.tv_sec = config_seconds; |
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.
Am I reading this right? If credential.timeout
is not defined in the config, we'll default to 5 minutes? So to avoid the timeout and get the legacy wait, we need to have credential.timeout=0
in the config. Not questioning, just confirming intent.
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.
Yes, that's the intent.
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.
Just a note for GCM.. this is a setting we should ignore ourselves :)
As in, GCM itself shouldn't have a timeout, since Git is the one doing the timeout and kill.
credential.c
Outdated
return -1; | ||
} | ||
} | ||
|
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'm assuming that if we get a single byte from GCM, we'll get the full response so we don't need to do something more fancy. GCM will either be stuck or not before the first byte is returned.
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 also my expectation. The response is not provided until after the "blocking" part of GCM is complete. @mjcheetham or @ldennington might be able to confirm for sure.
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.
GCM only writes back to stdout
once it has the complete result. If we've written anything back, we will soon exit.
credential.c
Outdated
|
||
if (!FD_ISSET(helper.out, &readfds)) { | ||
/* Timeout complete before helper.out has bytes to read. */ | ||
kill_child_command(&helper); |
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 bothers me a bit. I'm wondering if we should just insert a kill()
here and then let the existing call to finish_command()
at the bottom wait for the child. The existing finish code will end up calling wait_or_whine()
which normalizes the child's exit code, so we'll be consistent. And then it'll emit
the child_exit
and other cleanup.
You could also add a trace2_data after the kill()
to log that we sent the signal, but that is not strictly necessary.
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.
Also, then you won't need the kill_child_command()
function at all. That might make upstream integration easier.
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.
Interesting. I hadn't considered that, because I expected the behavior to be "kill the child then leave the method" and not to go to a later finish_command()
. I will play with this when I have a better testing environment ready.
builtin/gc.c
Outdated
@@ -1678,6 +1657,9 @@ static const char *get_frequency(enum schedule_priority schedule) | |||
} | |||
} | |||
|
|||
static const char *custom_background_config = | |||
"-c credential.interactive=false -c cred.askPass=false"; |
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 wonder if it's worth trying to upstream the concept of credential.interactive
, and have it mean "helpers should not interact with the user".
Right now, this is a GCM specific thing.
Alternatively, perhaps a core.interactive
or core.background
setting would make more sense? To indicate that "this instance of Git is running non-interactively or in the background", and that is a signal that helpers can pick up on to mean "no prompt"?
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 might be simpler to have core.background
as a single value. Then GCM or the ask-pass or whatever
code could do the right thing without us having to enumerate the various flags here.
Does |
Same question about Do we still need the timeout if GCM respects the suggested |
I could consider leaving it as a non-timeout case for foreground and set the config in the maintenance scheduler (like we are already doing for the interactivity bit).
Is that a thing? One thing I think happens is that background jobs don't have a TTY, so we won't get blocked on Git's request for a username/password (which during my local testing requires setting |
f27b3af
to
a25660f
Compare
I'll wait for @mjcheetham opinion here, but I'm wondering if we want to change the foreground behavior here.
I'm not sure. There are too many child processes with STDIN/OUT bound to a pipe from the parent process for me to casually trust |
What's even worse is that the Git prompt for username/password goes through |
End-to-End Testing ReportAfter some local testing in Linux helped identify some issues, I generated a Windows installer and ran it on my Windows machine. Along with an earlier version of the installer, I was able to find out this information:
I need to update this branch with the full learnings here, and add some tests now that we understand the code necessary to get the features we need. |
1ecfce3
to
8676615
Compare
nit: typo in commit message of "add new interactive config option": |
This change from microsoft#468 is causing multiple maintenance processes to get blocked on credentials instead of only one. The change did more harm than good. This reverts commit 95ed7f6.
8676615
to
840beed
Compare
i didn't see anything else. thanks! |
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 am optimistic that this will address the reported problems.
A couple of feedback comments about the commit messages:
- In the second commit's message: "caues" -> "cause", "modifed" -> "modified".
- The third commit's message mentions "GCM" without prior explanation of the acronym; I would recommend using "Git Credential Manager" here instead.
char *value; | ||
if (!git_config_get_maybe_bool("credential.interactive", &interactive) && | ||
!interactive) | ||
return -1; |
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.
Do we want to trace the fact that the interactive credential prompt was skipped?
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.
Interesting idea. I was thinking that the lack of the other region would be enough.
Do you propose using a trace2_printf()
? or what kind of indicator? I'm not familiar with an example of this kind of tracing.
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.
you could do something like a trace2_data_intmax(... "credential", "interactive/skipped", 1)
only log the true case.
73fe2ea
to
e0b672c
Compare
When scripts or background maintenance wish to perform HTTP(S) requests, there is a risk that our stored credentials might be invalid. At the moment, this causes the credential helper to ping the user and block the process. Even if the credential helper does not ping the user, Git falls back to the 'askpass' method, which includes a direct ping to the user via the terminal. Even setting the 'core.askPass' config as something like 'echo' will causes Git to fallback to a terminal prompt. It uses git_terminal_prompt(), which finds the terminal from the environment and ignores whether stdin has been redirected. This can also block the process awaiting input. Create a new config option to prevent user interaction, favoring a failure to a blocked process. The chosen name, 'credential.interactive', is taken from the config option used by Git Credential Manager to already avoid user interactivity, so there is already one credential helper that integrates with this option. However, older versions of Git Credential Manager also accepted other string values, including 'auto', 'never', and 'always'. The modern use is to use a boolean value, but we should still be careful that some users could have these non-booleans. Further, we should respect 'never' the same as 'false'. This is respected by the implementation and test, but not mentioned in the documentation. The implementation for the Git interactions takes place within credential_getpass(). The method prototype is modified to return an 'int' instead of 'void'. This allows us to detect that no attempt was made to fill the given credential, changing the single caller slightly. Also, a new trace2 region is added around the interactive portion of the credential request. This provides a way to measure the amount of time spent in that region for commands that _are_ interactive. It also makes a conventient way to test that the config option works with 'test_region'. Signed-off-by: Derrick Stolee <derrickstolee@github.com>
At the moment, some background jobs are getting blocked on credentials during the 'prefetch' task. This leads to other tasks, such as incremental repacks, getting blocked. Further, if a user manages to fix their credentials, then they still need to cancel the background process before their background maintenance can continue working. Update the background schedules for our four scheduler integrations to include these config options via '-c' options: * 'credential.interactive=false' will stop Git and some credential helpers from prompting in the UI (assuming the '-c' parameters are carried through and respected by GCM). * 'core.askPass=true' will replace the text fallback for a username and password into the 'true' command, which will return a success in its exit code, but Git will treat the empty string returned as an invalid password and move on. We can do some testing that the credentials are passed, at least in the systemd case due to writing the service files. Signed-off-by: Derrick Stolee <derrickstolee@github.com>
The 'scalar reconfigure' command is intended to update registered repos with the latest settings available. However, up to now we were not reregistering the repos with background maintenance. In particular, this meant that the background maintenance schedule would not be updated if there are improvements between versions. Be sure to register repos for maintenance during the reconfigure step. Signed-off-by: Derrick Stolee <derrickstolee@github.com>
In this commit, we added the 'credential.interactive=never' option to unattended scalar options. This should be changed to 'false' to match the modern use of this config option. But also, we have a test that requires using askpass to get credentials, but the test is in unattended mode. Fix that test to include 'credential.interactive=true' to bypass this issue.
e0b672c
to
e5459ea
Compare
already under review upstream.GVFS Protocol.
This set of patches has a little of both:
maintenance.lock
files that was only inmicrosoft/git
. The "fix" actually caused a worse situation where many background maintenance processes would pile up.credential.interactive
config as an official Git config and use it in Git to prevent any chance of a foreground username/password request. (This is borrowed from GCM, which already respects this.)scalar reconfigure
to executegit maintenance start
.Points 2-4 could maybe to upstream, but we shouldn't wait for that.
I've tested these features locally on Linux and Windows and double-checked that they will prevent the backup of maintenance processes when the credentials become invalid.