Documentation
¶
Index ¶
- Constants
- Variables
- func CleanupExpiredServices()
- func ExtractAppIDs(creds string) (appID, installationID string, err error)
- func GenerateCacheKey(creds string) string
- func Is403(err error) bool
- func Is404(err error) bool
- func SortInt64(s []int64)
- type ActionsClient
- type AppQuota
- type Client
- func NewClient(services *Services, metrics *telemetry.RateLimitMetrics) *Client
- func ResolveAndConnect(ctx context.Context, kube client.Client, pc *apisv1alpha1.ProviderConfig, ...) (*Client, error)
- func ResolveAndConnectNamespaced(ctx context.Context, kube client.Client, ref *xpv2.ProviderConfigReference, ...) (*Client, error)
- type CooldownError
- type DependabotClient
- type OrganizationsClient
- type RepoIDResolver
- type RepositoriesClient
- type RulesetsClient
- type Services
- type ServicesCache
- type TeamsClient
- type UsersClient
Constants ¶
const ( PickReasonOnlyCandidate = "only_candidate" PickReasonHighestRemaining = "highest_remaining" PickReasonRandomTiebreak = "random_tiebreak" )
Reason values returned by pick to label picker_picks_total. Stable across releases — operators may build dashboards keyed on them.
Variables ¶
var ErrAllAppsInCooldown = errors.New("all GitHub apps are in rate-limit cooldown")
ErrAllAppsInCooldown is the sentinel "every credential is in cooldown" error. Callers that only need to detect the condition use errors.Is(err, ErrAllAppsInCooldown). Callers that need the soonest retry hint use errors.As to unwrap a *CooldownError.
Functions ¶
func CleanupExpiredServices ¶
func CleanupExpiredServices()
CleanupExpiredServices removes expired clients from cache (optional background cleanup)
func ExtractAppIDs ¶
ExtractAppIDs returns the GitHub App ID and Installation ID from a credential string. The credential string format is "appID,installationID,privateKeyPEM"; PEM bodies are base64 plus dashes/newlines and never contain commas, so a 3-way split on the first two commas is sufficient. The returned values are kept as raw strings because they are used as Prometheus label values, not for arithmetic.
func GenerateCacheKey ¶
GenerateCacheKey creates a consistent cache key from credentials. The same key is used both for the client cache and for the per-app rate-limit quota pool, so that each unique credential blob has a single shared identity.
Types ¶
type ActionsClient ¶
type ActionsClient interface {
ListEnabledReposInOrg(ctx context.Context, owner string, opts *github.ListOptions) (*github.ActionsEnabledOnOrgRepos, *github.Response, error)
SetEnabledReposInOrg(ctx context.Context, owner string, repositoryIDs []int64) (*github.Response, error)
GetOrgSecret(ctx context.Context, org, name string) (*github.Secret, *github.Response, error)
ListSelectedReposForOrgSecret(ctx context.Context, org, name string, opts *github.ListOptions) (*github.SelectedReposList, *github.Response, error)
SetSelectedReposForOrgSecret(ctx context.Context, org, name string, ids []int64) (*github.Response, error)
GetOrgVariable(ctx context.Context, org, name string) (*github.ActionsVariable, *github.Response, error)
CreateOrgVariable(ctx context.Context, org string, variable github.ActionsCreateOrgVariableRequest) (*github.Response, error)
UpdateOrgVariable(ctx context.Context, org, name string, variable github.ActionsUpdateOrgVariableRequest) (*github.Response, error)
DeleteOrgVariable(ctx context.Context, org, name string) (*github.Response, error)
ListSelectedReposForOrgVariable(ctx context.Context, org, name string, opts *github.ListOptions) (*github.SelectedReposList, *github.Response, error)
ListOrganizationRunnerGroups(ctx context.Context, org string, opts *github.ListOrgRunnerGroupOptions) (*github.RunnerGroups, *github.Response, error)
CreateOrganizationRunnerGroup(ctx context.Context, org string, createReq github.CreateRunnerGroupRequest) (*github.RunnerGroup, *github.Response, error)
UpdateOrganizationRunnerGroup(ctx context.Context, org string, groupID int64, updateReq github.UpdateRunnerGroupRequest) (*github.RunnerGroup, *github.Response, error)
DeleteOrganizationRunnerGroup(ctx context.Context, org string, groupID int64) (*github.Response, error)
ListRepositoryAccessRunnerGroup(ctx context.Context, org string, groupID int64, opts *github.ListOptions) (*github.ListRepositories, *github.Response, error)
SetRepositoryAccessRunnerGroup(ctx context.Context, org string, groupID int64, ids github.SetRepoAccessRunnerGroupRequest) (*github.Response, error)
}
type AppQuota ¶
type AppQuota struct {
Remaining int
CooldownUntil time.Time
// ConsecutiveFailures counts (nil-response, non-nil-error) outcomes
// since the last successful HTTP response. Drives the exponential
// backoff for unhealthyCooldown. Reset to zero on any non-nil resp.
ConsecutiveFailures int
}
AppQuota is a snapshot of the last-known rate-limit state for one credential.
type Client ¶
type Client struct {
*Services
// contains filtered or unexported fields
}
Client is the GitHub client controllers use. It wraps a *Services with per-credential labels so every API call is recorded into Prometheus telemetry and the per-credential cooldown pool.
func NewClient ¶
func NewClient(services *Services, metrics *telemetry.RateLimitMetrics) *Client
NewClient builds a Client around a *Services (typically returned by NewCachedServices) so every wrapped call carries rate-limit telemetry.
func ResolveAndConnect ¶
func ResolveAndConnect(ctx context.Context, kube client.Client, pc *apisv1alpha1.ProviderConfig, metrics *telemetry.RateLimitMetrics, org string) (*Client, error)
ResolveAndConnect resolves every credential entry on pc (Credentials and AdditionalCredentials), picks the one with the most available rate-limit quota, builds a cached GitHub client for it, and returns a wrapped Client ready for use by a controller.
On a fully-exhausted pool the returned error wraps ErrAllAppsInCooldown so callers can decide whether to requeue or surface the error.
func ResolveAndConnectNamespaced ¶
func ResolveAndConnectNamespaced(ctx context.Context, kube client.Client, ref *xpv2.ProviderConfigReference, namespace string, metrics *telemetry.RateLimitMetrics, org string) (*Client, error)
ResolveAndConnectNamespaced is ResolveAndConnect for a namespaced managed resource. ref is the resource's providerConfigRef, which names either a ProviderConfig in namespace or a ClusterProviderConfig.
func (*Client) WithRateLimitTracking ¶
WithRateLimitTracking returns a new Client whose service wrappers tag every API response with the given identifiers:
- org is the GitHub organization for the Prometheus organization label;
- appID and installationID are the GitHub App ID and Installation ID, used as the app_id and app_installation_id metric labels;
- cacheKey is the credential cache key, used internally by the per-app quota pool to track 429s and steer future picks.
appID/installationID and cacheKey are separate so that the metric labels stay human-meaningful while the pool keeps its stable internal identity.
type CooldownError ¶
CooldownError is returned when every credential in the pool is currently cooling down. RetryAt carries the soonest expiry time so the caller can issue a sensible RequeueAfter or backoff. Unwraps to ErrAllAppsInCooldown so legacy errors.Is checks keep working.
func (*CooldownError) Error ¶
func (e *CooldownError) Error() string
func (*CooldownError) Unwrap ¶
func (e *CooldownError) Unwrap() error
type DependabotClient ¶
type DependabotClient interface {
GetOrgSecret(ctx context.Context, org, name string) (*github.Secret, *github.Response, error)
ListSelectedReposForOrgSecret(ctx context.Context, org, name string, opts *github.ListOptions) (*github.SelectedReposList, *github.Response, error)
SetSelectedReposForOrgSecret(ctx context.Context, org, name string, ids []int64) (*github.Response, error)
}
type OrganizationsClient ¶
type OrganizationsClient interface {
Get(ctx context.Context, org string) (*github.Organization, *github.Response, error)
Edit(ctx context.Context, name string, org *github.Organization) (*github.Organization, *github.Response, error)
GetOrgMembership(ctx context.Context, user, org string) (*github.Membership, *github.Response, error)
CreateOrgInvitation(ctx context.Context, org string, opts *github.CreateOrgInvitationOptions) (*github.Invitation, *github.Response, error)
EditOrgMembership(ctx context.Context, user, org string, membership *github.Membership) (*github.Membership, *github.Response, error)
RemoveOrgMembership(ctx context.Context, user, org string) (*github.Response, error)
ListHooks(ctx context.Context, org string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error)
GetHook(ctx context.Context, org string, id int64) (*github.Hook, *github.Response, error)
CreateHook(ctx context.Context, org string, hook *github.Hook) (*github.Hook, *github.Response, error)
EditHook(ctx context.Context, org string, id int64, hook *github.Hook) (*github.Hook, *github.Response, error)
DeleteHook(ctx context.Context, org string, id int64) (*github.Response, error)
}
type RepoIDResolver ¶
type RepoIDResolver struct {
// contains filtered or unexported fields
}
RepoIDResolver resolves repository names to numeric IDs once per reconcile. Avoids hitting Repositories.Get more than once for the same name when both Observe and Update need IDs for the same SelectedRepositories list.
func NewRepoIDResolver ¶
func NewRepoIDResolver(gh *Client, org string) *RepoIDResolver
NewRepoIDResolver returns a RepoIDResolver for repositories in org.
func (*RepoIDResolver) BatchGetIDs ¶
BatchGetIDs resolves names to repository IDs, preserving order.
type RepositoriesClient ¶
type RepositoriesClient interface {
Get(ctx context.Context, owner, repo string) (*github.Repository, *github.Response, error)
Edit(ctx context.Context, owner, repo string, repository *github.Repository) (*github.Repository, *github.Response, error)
ListTeams(ctx context.Context, owner string, repo string, opts *github.ListOptions) ([]*github.Team, *github.Response, error)
ListCollaborators(ctx context.Context, owner, repo string, opts *github.ListCollaboratorsOptions) ([]*github.User, *github.Response, error)
ListInvitations(ctx context.Context, owner, repo string, opts *github.ListOptions) ([]*github.RepositoryInvitation, *github.Response, error)
Create(ctx context.Context, org string, repo *github.Repository) (*github.Repository, *github.Response, error)
CreateFromTemplate(ctx context.Context, templateOwner, templateRepo string, templateRepoReq github.TemplateRepoRequest) (*github.Repository, *github.Response, error)
CreateFork(ctx context.Context, owner, repo string, opts *github.RepositoryCreateForkOptions) (*github.Repository, *github.Response, error)
AddCollaborator(ctx context.Context, owner, repo, user string, opts *github.RepositoryAddCollaboratorOptions) (*github.CollaboratorInvitation, *github.Response, error)
RemoveCollaborator(ctx context.Context, owner, repo, user string) (*github.Response, error)
GetPermissionLevel(ctx context.Context, owner, repo, user string) (*github.RepositoryPermissionLevel, *github.Response, error)
Delete(ctx context.Context, owner, repo string) (*github.Response, error)
CreateHook(ctx context.Context, owner, repo string, hook *github.Hook) (*github.Hook, *github.Response, error)
EditHook(ctx context.Context, owner, repo string, id int64, hook *github.Hook) (*github.Hook, *github.Response, error)
DeleteHook(ctx context.Context, owner, repo string, id int64) (*github.Response, error)
ListHooks(ctx context.Context, owner, repo string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error)
ListBranches(ctx context.Context, owner, repo string, opts *github.BranchListOptions) ([]*github.Branch, *github.Response, error)
GetBranch(ctx context.Context, owner, repo, branch string, maxRedirects int) (*github.Branch, *github.Response, error)
GetBranchProtection(ctx context.Context, owner, repo, branch string) (*github.Protection, *github.Response, error)
UpdateBranchProtection(ctx context.Context, owner, repo, branch string, preq *github.ProtectionRequest) (*github.Protection, *github.Response, error)
RemoveBranchProtection(ctx context.Context, owner, repo, branch string) (*github.Response, error)
RequireSignaturesOnProtectedBranch(ctx context.Context, owner, repo, branch string) (*github.SignaturesProtectedBranch, *github.Response, error)
OptionalSignaturesOnProtectedBranch(ctx context.Context, owner, repo, branch string) (*github.Response, error)
ReplaceAllTopics(ctx context.Context, owner, repo string, topics []string) ([]string, *github.Response, error)
}
type RulesetsClient ¶
type RulesetsClient interface {
GetAllRulesets(ctx context.Context, owner, repo string, opts *github.RepositoryListRulesetsOptions) ([]*rulesets.Ruleset, *github.Response, error)
GetRuleset(ctx context.Context, owner, repo string, rulesetID int64, includesParents bool) (*rulesets.Ruleset, *github.Response, error)
CreateRuleset(ctx context.Context, owner, repo string, ruleset rulesets.Ruleset) (*rulesets.Ruleset, *github.Response, error)
UpdateRuleset(ctx context.Context, owner, repo string, rulesetID int64, ruleset rulesets.Ruleset) (*rulesets.Ruleset, *github.Response, error)
DeleteRuleset(ctx context.Context, owner, repo string, rulesetID int64) (*github.Response, error)
}
RulesetsClient reads and writes repository rulesets with the types of package rulesets.
type Services ¶
type Services struct {
Actions ActionsClient
Dependabot DependabotClient
Organizations OrganizationsClient
Users UsersClient
Teams TeamsClient
Repositories RepositoriesClient
Rulesets RulesetsClient
}
func NewCachedServices ¶
NewCachedServices creates a new cached GitHub client that reuses tokens
type ServicesCache ¶
type ServicesCache struct {
// contains filtered or unexported fields
}
ServicesCache caches *Services keyed by credential hash so that installation tokens are reused across calls within a TTL.
type TeamsClient ¶
type TeamsClient interface {
GetTeamBySlug(ctx context.Context, org, slug string) (*github.Team, *github.Response, error)
ListTeamMembersBySlug(ctx context.Context, org, slug string, opts *github.TeamListTeamMembersOptions) ([]*github.User, *github.Response, error)
CreateTeam(ctx context.Context, org string, team github.NewTeam) (*github.Team, *github.Response, error)
AddTeamMembershipBySlug(ctx context.Context, org, slug, user string, opts *github.TeamAddTeamMembershipOptions) (*github.Membership, *github.Response, error)
RemoveTeamMembershipBySlug(ctx context.Context, org, slug, user string) (*github.Response, error)
ListPendingTeamInvitationsBySlug(ctx context.Context, org, slug string, opts *github.ListOptions) ([]*github.Invitation, *github.Response, error)
ListChildTeamsByParentSlug(ctx context.Context, org, slug string, opts *github.ListOptions) ([]*github.Team, *github.Response, error)
EditTeamBySlug(ctx context.Context, org, slug string, team github.NewTeam, removeParent bool) (*github.Team, *github.Response, error)
DeleteTeamBySlug(ctx context.Context, org, slug string) (*github.Response, error)
AddTeamRepoBySlug(ctx context.Context, org, slug, owner, repo string, opts *github.TeamAddTeamRepoOptions) (*github.Response, error)
RemoveTeamRepoBySlug(ctx context.Context, org, slug, owner, repo string) (*github.Response, error)
IsTeamRepoBySlug(ctx context.Context, org, slug, owner, repo string) (*github.Repository, *github.Response, error)
}