Skip to content

fix(coderd): don't hang on first gitauth clone #7331

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

Merged
merged 1 commit into from
May 1, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
fix(coderd): don't hang on first gitauth clone
Previously, the `coder git ssh` command would hang on the API, which was endlessly
polling the database for oauth tokens that expire in the future.

Some oAuth implementations (including GitHub by default) will not give back a
token expiry date, and the absence of a token expiry date became the zero
date in the database.

Follow-up calls to `git clone` would succeed because the non-listen path doesn't
check expiry, perhaps by mistake.

In addition to fixing the zero date issue, this PR removes the PubSub
which added too much complexity when the polling interval is 1 second.
  • Loading branch information
ammario committed May 1, 2023
commit 0b4eacd917404305e8ec6dcbf174352b8d3adbe9
47 changes: 9 additions & 38 deletions coderd/workspaceagents.go
Original file line number Diff line number Diff line change
Expand Up @@ -1737,40 +1737,15 @@ func (api *API) workspaceAgentsGitAuth(rw http.ResponseWriter, r *http.Request)
}

if listen {
// If listening we await a new token...
authChan := make(chan struct{}, 1)
cancelFunc, err := api.Pubsub.Subscribe("gitauth", func(ctx context.Context, message []byte) {
ids := strings.Split(string(message), "|")
if len(ids) != 2 {
return
}
if ids[0] != gitAuthConfig.ID {
return
}
if ids[1] != workspace.OwnerID.String() {
return
}
select {
case authChan <- struct{}{}:
default:
}
})
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Failed to listen for git auth token.",
Detail: err.Error(),
})
return
}
defer cancelFunc()
// Since we're ticking frequently and this sign-in operation is rare,
// we are OK with polling to avoid the complexity of pubsub.
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
case <-authChan:
}
gitAuthLink, err := api.Database.GetGitAuthLink(ctx, database.GetGitAuthLinkParams{
ProviderID: gitAuthConfig.ID,
Expand All @@ -1786,7 +1761,12 @@ func (api *API) workspaceAgentsGitAuth(rw http.ResponseWriter, r *http.Request)
})
return
}
if gitAuthLink.OAuthExpiry.Before(database.Now()) {

// Expiry may be unset if the application doesn't configure tokens
// to expire.
// See
// https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app.
if gitAuthLink.OAuthExpiry.Before(database.Now()) && !gitAuthLink.OAuthExpiry.IsZero() {
continue
}
if gitAuthConfig.ValidateURL != "" {
Expand Down Expand Up @@ -1932,20 +1912,11 @@ func (api *API) gitAuthCallback(gitAuthConfig *gitauth.Config) http.HandlerFunc
}
}

err = api.Pubsub.Publish("gitauth", []byte(fmt.Sprintf("%s|%s", gitAuthConfig.ID, apiKey.UserID)))
if err != nil {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "Failed to publish auth update.",
Detail: err.Error(),
})
return
}

redirect := state.Redirect
if redirect == "" {
// This is a nicely rendered screen on the frontend
redirect = "/gitauth"
}
// This is a nicely rendered screen on the frontend
http.Redirect(rw, r, redirect, http.StatusTemporaryRedirect)
}
}
Expand Down