-
Notifications
You must be signed in to change notification settings - Fork 113
refactor: Improve GitHub App installation ID retrieval #2154
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
base: main
Are you sure you want to change the base?
refactor: Improve GitHub App installation ID retrieval #2154
Conversation
- Removed listing of all installations. - Fetched the installation directly using the repository URL. - Added fallback to find the organization installation if the repository installation is not found. - Enhanced error handling for token retrieval. - Updated tests to reflect the changes. Signed-off-by: Chmouel Boudjnah <[email protected]>
This fix will help us resolve this issue: https://issues.redhat.com/browse/KONFLUX-5929 |
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.
What's missing in this PR (why is it in draft mode)?
continue | ||
} | ||
} | ||
exist, err := ip.matchRepos(ctx) |
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.
with ip.matchRepos no longer used in this file, we can move it to the test file as it's now used only there.
} | ||
owner := pathParts[0] | ||
repoName := pathParts[1] | ||
|
||
apiURL := *ip.ghClient.APIURL |
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.
apiURL := *ip.ghClient.APIURL | |
if ip.ghClient.APIURL == nil { | |
return "", "", 0, fmt.Errorf("github client APIURL is nil") | |
} | |
apiURL := *ip.ghClient.APIURL |
if enterpriseHost != "" { | ||
// NOTE: Hopefully this works even when the GHE URL is on another host than the API URL | ||
apiURL = "https://" + enterpriseHost + "/api/v3" |
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.
apiURL = "https://" + enterpriseHost + "/api/v3" | |
apiURL = fmt.Sprintf("https://%s/api/v3", strings.TrimSuffix(enterpriseHost, "/")) |
owner := pathParts[0] | ||
repoName := pathParts[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.
Should we check them for being empty? Or is it supposed to be always present?
if owner == "" || repoName == "" { return "", "", 0, fmt.Errorf("invalid repository URL: owner or repo name is empty") }
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's not bad to be on the safe side, but tbh that's probably not a concern. It would require there to be //
in the path which, while possibly accepted by github, is never going to match the events from Github and therefore isn't valid as a Respository CR.
not enough time to work on it for now (and it wasn't prioritized) and generally we don't have enough reviewer in pac so i put thing as draft pr so people don't get stress until i am sure it work... |
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 is so much cleaner and I think may significantly reduce the number of API calls.
Also, it looks like these API calls are not being included in the pipelines_as_code_git_provider_api_request_count
metrics. I don't think that necessarily needs to be clubbed into this PR, but we'll need to fix that as well
pathParts := strings.Split(strings.Trim(repoURL.Path, "/"), "/") | ||
if len(pathParts) < 2 { | ||
return "", "", 0, fmt.Errorf("invalid repository URL path: %s", repoURL.Path) | ||
} |
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.
Nitpick
Should this be checking for longer paths as well? I know Gitlab supports sub-projects but I don't think Github would support /foo/bar/baz
. It technically does resolve for example https://github.com/openshift-pipelines//pipelines-as-code
with the two slashes, but since the Repository CR's URL semantically has to match the URL sent in the Github event payload I don't think that //
will be valid in the RepositoryCR's URL in the first place.
pathParts := strings.Split(strings.Trim(repoURL.Path, "/"), "/") | |
if len(pathParts) < 2 { | |
return "", "", 0, fmt.Errorf("invalid repository URL path: %s", repoURL.Path) | |
} | |
pathParts := strings.Split(strings.Trim(repoURL.Path, "/"), "/") | |
if len(pathParts) != 2 { | |
return "", "", 0, fmt.Errorf("invalid repository URL path: expected '<organization>/<repository>', got: %s", repoURL.Path) | |
} |
owner := pathParts[0] | ||
repoName := pathParts[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.
It's not bad to be on the safe side, but tbh that's probably not a concern. It would require there to be //
in the path which, while possibly accepted by github, is never going to match the events from Github and therefore isn't valid as a Respository CR.
// Directly get the installation for the repository | ||
installation, _, err := client.Apps.FindRepositoryInstallation(ctx, owner, repoName) | ||
if err != nil { | ||
// Fallback to finding organization installation if repository installation is not found | ||
installation, _, err = client.Apps.FindOrganizationInstallation(ctx, owner) | ||
if err != nil { |
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.
Change needed:
The Installation could also be installed for the User as well, so we'll also need to check client.Apps.FindUserInstallation()
as well. If only these methods all had the same signature we could just iterate through a list of the methods, alas 😖
if installation.ID == nil { | ||
return "", "", 0, fmt.Errorf("installation ID is nil") | ||
} |
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.
Nit pick suggestion - in case a user is reading this, a little more verbosity here could help understand what this error means. I don't know when (if ever) the installation ID might be nil
but I can only assume that it would indicate a bug in how we use the API or a bug in the github-go library
if installation.ID == nil { | |
return "", "", 0, fmt.Errorf("installation ID is nil") | |
} | |
if installation.ID == nil { | |
return "", "", 0, fmt.Errorf("Github App installation found but contained no ID. This is likely a bug") | |
} |
installationID := *installation.ID | ||
token, err := ip.ghClient.GetAppToken(ctx, ip.run.Clients.Kube, enterpriseHost, installationID, ip.namespace) | ||
if err != nil { | ||
logger.Warnf("Could not get a token for installation ID %d: %v", installationID, err) |
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.
Should this be an event instead of just a log? Not sure when events are desirable instead of just logs
mux.HandleFunc(fmt.Sprintf("/orgs/%s/installation", orgName), func(w http.ResponseWriter, _ *http.Request) { | ||
_, _ = fmt.Fprintf(w, `{"id": %d}`, wantID) | ||
}) |
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 you mind adding a test case for the fallback(s) as well?
let's wait for #2199 to ship first to make sure we can instrument this |
Changes
Submitter Checklist
📝 Ensure your commit message is clear and informative. Refer to the How to write a git commit message guide. Include the commit message in the PR body rather than linking to an external site (e.g., Jira ticket).
♽ Run make test lint before submitting a PR to avoid unnecessary CI processing. Consider installing pre-commit and running pre-commit install in the repository root for an efficient workflow.
✨ We use linters to maintain clean and consistent code. Run make lint before submitting a PR. Some linters offer a --fix mode, executable with make fix-linters (ensure markdownlint and golangci-lint are installed).
📖 Document any user-facing features or changes in behavior.
🧪 While 100% coverage isn't required, we encourage unit tests for code changes where possible.
🎁 If feasible, add an end-to-end test. See README for details.
🔎 Address any CI test flakiness before merging, or provide a valid reason to bypass it (e.g., token rate limitations).
If adding a provider feature, fill in the following details:
(update the provider documentation accordingly)