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

Webhook handler now processes installation_repositories events #3447

Merged
merged 3 commits into from
Jun 5, 2024
Merged

Conversation

blkt
Copy link
Contributor

@blkt blkt commented May 28, 2024

Summary

Such events are triggered when a new repository is added to or removed
from the list of repositories accessible to the app.

Fixes #3267

Change Type

  • Bug fix (resolves an issue without affecting existing features)
  • Feature (adds new functionality without breaking changes)
  • Breaking change (may impact existing functionalities or require documentation updates)
  • Documentation (updates or additions to documentation)
  • Refactoring or test improvements (no bug fixes or new functionality)

Testing

Outline how the changes were tested, including steps to reproduce and any relevant configurations.
Attach screenshots if helpful.

Review Checklist:

  • Reviewed my own code for quality and clarity.
  • Added comments to complex or tricky code sections.
  • Updated any affected documentation.
  • Included tests that validate the fix or feature.
  • Checked that related changes are merged.

@coveralls
Copy link

coveralls commented May 30, 2024

Coverage Status

coverage: 53.132% (+0.5%) from 52.676%
when pulling 432ec11 on issue-3267
into 54d663d on main.

@blkt blkt force-pushed the issue-3267 branch 3 times, most recently from 931d80c to 979e5c7 Compare May 31, 2024 15:15
Comment on lines +1044 to +1046
// We might want to ignore this case since we can only delete
// repositories there were previously registered, which would
// be deleted by means of "meta" and "repository" events as
// well.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is interesting: we run into a race condition if, for a given repository that was removed from the org, we get a meta before the installation_repositories event, or vice versa. The first deletion succeeds, while the second fails.

error deleting repository from DB: error retrieving repository: sql: no rows in result set

This is not a problem right now, as it is handled correctly by logging some errors, but we do retry the deletion a handful of times, which might cause issues at scale.

Copy link
Contributor

Choose a reason for hiding this comment

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

is the meta event for the webhook/installation and the installation_repositories for the repos only? Could we process one or the other? I think in either case they could be solved by a single db lookup, no? (if we are to add and the repo is already there, then skip, if we are to delete and the repo is already deleted, skip).

Either way, nice that you caught this issue already.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have to test what happens if we revoke access to a repository without deleting it from GitHub.

I believe that we don't get the "meta" event in such a case, i.e. we have to handle "removed" action for "installation_repositories" events as well.

I'll test it manually and reply here.

Comment on lines +27 to +50
// RepoReconcilerEvent is an event that is sent to the reconciler topic
type RepoReconcilerEvent struct {
// Project is the project that the event is relevant to
Project uuid.UUID `json:"project"`
// Repository is the repository to be reconciled
Repository int64 `json:"repository" validate:"gte=0"`
}

// NewRepoReconcilerMessage creates a new repos init event
func NewRepoReconcilerMessage(providerID uuid.UUID, repoID int64, projectID uuid.UUID) (*message.Message, error) {
evt := &RepoReconcilerEvent{
Repository: repoID,
Project: projectID,
}

evtStr, err := json.Marshal(evt)
if err != nil {
return nil, fmt.Errorf("error marshalling init event: %w", err)
}

msg := message.NewMessage(uuid.New().String(), evtStr)
msg.Metadata.Set("provider_id", providerID.String())
return msg, nil
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was moved here to solve a circular dependency between internal/reconcilers/reconcilers.go and internal/service/service.go

Copy link
Contributor

@jhrozek jhrozek left a comment

Choose a reason for hiding this comment

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

Nice work. Left some comments inline, but overall this PR is shaping quite nicely!


installationID := event.GetInstallation().GetID()
installation, err := s.store.GetInstallationIDByAppID(ctx, installationID)
if errors.Is(err, sql.ErrNoRows) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we special-case this error somewhere?
I also wonder, does an unclaimed installation have an installation ID? (I think yes, but worth testing)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not following, what is an unclaimed installation?

Copy link
Contributor

Choose a reason for hiding this comment

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

The one you create without logging to minder first, e.g by going to https://github.com/apps/minder-instance/installations/new

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll test this.

}
if !installation.ProjectID.Valid {
return nil, errors.New("invalid project id")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

we should also test how these behave for an unclaimed install - I /think/ it should be OK as we create this special project (as we saw during pair-session on Friday), but worth testing.

ctx,
repo,
event.GetAction(),
events.TopicQueueReconcileEntityAdd,
Copy link
Contributor

Choose a reason for hiding this comment

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

can the topic ever be something else (asking if it needs to be a parameter)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, this can only be a events.TopicQueueReconcileEntityAdd.
Initially the routine was just one, this is a leftover.

Comment on lines +1044 to +1046
// We might want to ignore this case since we can only delete
// repositories there were previously registered, which would
// be deleted by means of "meta" and "repository" events as
// well.
Copy link
Contributor

Choose a reason for hiding this comment

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

is the meta event for the webhook/installation and the installation_repositories for the repos only? Could we process one or the other? I think in either case they could be solved by a single db lookup, no? (if we are to add and the repo is already there, then skip, if we are to delete and the repo is already deleted, skip).

Either way, nice that you caught this issue already.

Such events are triggered when a new repository is added to or removed
from the list of repositories accessible to the app.
@blkt blkt marked this pull request as ready for review June 4, 2024 11:49
@blkt blkt requested a review from a team as a code owner June 4, 2024 11:49
Copy link
Contributor Author

@blkt blkt left a comment

Choose a reason for hiding this comment

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

Here we go! 🚀


installationID := event.GetInstallation().GetID()
installation, err := s.store.GetInstallationIDByAppID(ctx, installationID)
if errors.Is(err, sql.ErrNoRows) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not following, what is an unclaimed installation?

ctx,
repo,
event.GetAction(),
events.TopicQueueReconcileEntityAdd,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, this can only be a events.TopicQueueReconcileEntityAdd.
Initially the routine was just one, this is a leftover.

Comment on lines +1044 to +1046
// We might want to ignore this case since we can only delete
// repositories there were previously registered, which would
// be deleted by means of "meta" and "repository" events as
// well.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have to test what happens if we revoke access to a repository without deleting it from GitHub.

I believe that we don't get the "meta" event in such a case, i.e. we have to handle "removed" action for "installation_repositories" events as well.

I'll test it manually and reply here.

@@ -37,6 +37,7 @@ import (
mockmanager "github.com/stacklok/minder/internal/providers/manager/mock"
ghrepo "github.com/stacklok/minder/internal/repositories/github"
mockghrepo "github.com/stacklok/minder/internal/repositories/github/mock"
rf "github.com/stacklok/minder/internal/repositories/github/mock/fixtures"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The diff for this file should just be refactoring, I moved helper routines to this package.

topic: installations.ProviderInstallationTopic,
statusCode: http.StatusOK,
queued: nil,
},

// installation repositories events
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Most of the diff for this file is refactoring, relevant tests are here.

@blkt
Copy link
Contributor Author

blkt commented Jun 4, 2024

@jhrozek I'm afraid that handling deletion is going to be trickier than we anticipated.
We might need to have a specific code path for deleting repositories that are not accessible anymore.

See this issue #3518

RepoEvent struct was added to avoid polluting EntityInfoWrapper
further.
Copy link
Contributor

@jhrozek jhrozek left a comment

Choose a reason for hiding this comment

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

This is great. I kicked the coverage tests because they seem to be failing while the "normal" unit test run succeeded, so it might be a fluke.

@blkt blkt merged commit 936892a into main Jun 5, 2024
20 checks passed
@blkt blkt deleted the issue-3267 branch June 5, 2024 08:49
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.

Add logic for handling added repositories to the GitHub App webhook handler
3 participants