From cec89c5b7b97a7074145486f7ae4448d63259bb2 Mon Sep 17 00:00:00 2001 From: Francesco Renzi Date: Wed, 26 Nov 2025 16:05:39 +0100 Subject: [PATCH] Add godoc comments and unexport (#27) * Add separate constructors for each credential type * Add godoc comments to client * Unexport GitHubConfig * Add godoc comments for listener package * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- client.go | 110 ++++++++++++++++------ client_test.go | 212 +++++++++++++++++++++---------------------- config.go | 58 ++++++------ config_test.go | 164 ++++++++++++++++----------------- listener/listener.go | 15 ++- 5 files changed, 309 insertions(+), 250 deletions(-) diff --git a/client.go b/client.go index f4d6607..dd31472 100644 --- a/client.go +++ b/client.go @@ -61,9 +61,11 @@ func (v *atomicValue[T]) Swap(new T) (old T) { return v.v.Swap(new).(T) } -// HeaderScaleSetMaxCapacity used to propagate capacity information to the back-end +// HeaderScaleSetMaxCapacity is used to propagate the scale set max +// capacity when polling for messages. const HeaderScaleSetMaxCapacity = "X-ScaleSetMaxCapacity" +// Client implements a GitHub Actions Scale Set client. type Client struct { httpClient *http.Client @@ -75,8 +77,8 @@ type Client struct { retryMax int retryWaitMax time.Duration - creds *ActionsAuth - config *GitHubConfig + creds *actionsAuth + config *gitHubConfig logger *slog.Logger userAgent atomicValue[string] @@ -87,6 +89,7 @@ type Client struct { proxyFunc ProxyFunc } +// GitHubAppAuth contains the GitHub App authentication credentials. All fields are required. type GitHubAppAuth struct { // ClientID is the Client ID of the application (app id also works) ClientID string @@ -96,6 +99,7 @@ type GitHubAppAuth struct { PrivateKey string } +// Validate returns an error if any required field is missing. func (a *GitHubAppAuth) Validate() error { if a.ClientID == "" { return fmt.Errorf("client ID is required") @@ -109,17 +113,20 @@ func (a *GitHubAppAuth) Validate() error { return nil } -type ActionsAuth struct { - // AppCreds is the GitHub App credentials - App *GitHubAppAuth +type actionsAuth struct { + // app is the GitHub app credentials + app *GitHubAppAuth // GitHub PAT - Token string + token string } +// ProxyFunc defines the function signature for a proxy function. type ProxyFunc func(req *http.Request) (*url.URL, error) +// Option defines a functional option for configuring the Client. type Option func(*Client) +// UserAgentInfo contains information for constructing the user agent string. type UserAgentInfo struct { // System is the name of the scale set implementation System string @@ -152,44 +159,66 @@ func (u UserAgentInfo) String() string { ) } +// WithLogger sets a custom logger for the Client. func WithLogger(logger slog.Logger) Option { return func(c *Client) { c.logger = &logger } } +// WithRetryMax sets the maximum number of retries for the Client. func WithRetryMax(retryMax int) Option { return func(c *Client) { c.retryMax = retryMax } } +// WithRetryWaitMax sets the maximum wait time between retries for the Client. func WithRetryWaitMax(retryWaitMax time.Duration) Option { return func(c *Client) { c.retryWaitMax = retryWaitMax } } +// WithRootCAs sets custom root certificate authorities for the Client. func WithRootCAs(rootCAs *x509.CertPool) Option { return func(c *Client) { c.rootCAs = rootCAs } } +// WithoutTLSVerify disables TLS certificate verification for the Client. func WithoutTLSVerify() Option { return func(c *Client) { c.tlsInsecureSkipVerify = true } } +// WithProxy sets a custom proxy function for the Client. func WithProxy(proxyFunc ProxyFunc) Option { return func(c *Client) { c.proxyFunc = proxyFunc } } -func NewClient(githubConfigURL string, creds *ActionsAuth, options ...Option) (*Client, error) { - config, err := ParseGitHubConfigFromURL(githubConfigURL) +// NewClientWithGitHubApp creates a new Client using GitHub App credentials. +func NewClientWithGitHubApp(githubConfigURL string, appCreds *GitHubAppAuth, options ...Option) (*Client, error) { + creds := &actionsAuth{ + app: appCreds, + } + return newClient(githubConfigURL, creds, options...) +} + +// NewClientWithPersonalAccessToken creates a new Client using a personal access token. +func NewClientWithPersonalAccessToken(githubConfigURL, personalAccessToken string, options ...Option) (*Client, error) { + creds := &actionsAuth{ + token: personalAccessToken, + } + return newClient(githubConfigURL, creds, options...) +} + +func newClient(githubConfigURL string, creds *actionsAuth, options ...Option) (*Client, error) { + config, err := parseGitHubConfigFromURL(githubConfigURL) if err != nil { return nil, fmt.Errorf("failed to parse githubConfigURL: %w", err) } @@ -279,7 +308,7 @@ func (c *Client) do(req *http.Request) (*http.Response, error) { } func (c *Client) newGitHubAPIRequest(ctx context.Context, method, path string, body io.Reader) (*http.Request, error) { - u := c.config.GitHubAPIURL(path) + u := c.config.gitHubAPIURL(path) req, err := http.NewRequestWithContext(ctx, method, u.String(), body) if err != nil { return nil, fmt.Errorf("failed to create new GitHub API request: %w", err) @@ -331,6 +360,7 @@ func (c *Client) newActionsServiceRequest(ctx context.Context, method, path stri return req, nil } +// GetRunnerScaleSet fetches a runner scale set by its name within a runner group. func (c *Client) GetRunnerScaleSet(ctx context.Context, runnerGroupID int, runnerScaleSetName string) (*RunnerScaleSet, error) { path := fmt.Sprintf("/%s?runnerGroupId=%d&name=%s", scaleSetEndpoint, runnerGroupID, runnerScaleSetName) req, err := c.newActionsServiceRequest(ctx, http.MethodGet, path, nil) @@ -371,6 +401,7 @@ func (c *Client) GetRunnerScaleSet(ctx context.Context, runnerGroupID int, runne return &runnerScaleSetList.RunnerScaleSets[0], nil } +// GetRunnerScaleSetByID fetches a runner scale set by its ID. func (c *Client) GetRunnerScaleSetByID(ctx context.Context, runnerScaleSetID uint64) (*RunnerScaleSet, error) { path := fmt.Sprintf("/%s/%d", scaleSetEndpoint, runnerScaleSetID) req, err := c.newActionsServiceRequest(ctx, http.MethodGet, path, nil) @@ -399,6 +430,7 @@ func (c *Client) GetRunnerScaleSetByID(ctx context.Context, runnerScaleSetID uin return runnerScaleSet, nil } +// GetRunnerGroupByName fetches a runner group by its name. func (c *Client) GetRunnerGroupByName(ctx context.Context, runnerGroup string) (*RunnerGroup, error) { path := fmt.Sprintf("/_apis/runtime/runnergroups/?groupName=%s", runnerGroup) req, err := c.newActionsServiceRequest(ctx, http.MethodGet, path, nil) @@ -456,6 +488,7 @@ func (c *Client) GetRunnerGroupByName(ctx context.Context, runnerGroup string) ( return &runnerGroupList.RunnerGroups[0], nil } +// CreateRunnerScaleSet creates a new runner scale set. Note that runner scale set names must be unique within a runner group. func (c *Client) CreateRunnerScaleSet(ctx context.Context, runnerScaleSet *RunnerScaleSet) (*RunnerScaleSet, error) { body, err := json.Marshal(runnerScaleSet) if err != nil { @@ -486,6 +519,7 @@ func (c *Client) CreateRunnerScaleSet(ctx context.Context, runnerScaleSet *Runne return createdRunnerScaleSet, nil } +// UpdateRunnerScaleSet updates an existing runner scale set. func (c *Client) UpdateRunnerScaleSet(ctx context.Context, runnerScaleSetID int, runnerScaleSet *RunnerScaleSet) (*RunnerScaleSet, error) { path := fmt.Sprintf("%s/%d", scaleSetEndpoint, runnerScaleSetID) @@ -519,6 +553,7 @@ func (c *Client) UpdateRunnerScaleSet(ctx context.Context, runnerScaleSetID int, return updatedRunnerScaleSet, nil } +// DeleteRunnerScaleSet deletes a runner scale set by its ID. func (c *Client) DeleteRunnerScaleSet(ctx context.Context, runnerScaleSetID int) error { path := fmt.Sprintf("/%s/%d", scaleSetEndpoint, runnerScaleSetID) req, err := c.newActionsServiceRequest(ctx, http.MethodDelete, path, nil) @@ -539,7 +574,10 @@ func (c *Client) DeleteRunnerScaleSet(ctx context.Context, runnerScaleSetID int) return nil } -// GetMessage fetches a message from the runner scale set message queue. +// GetMessage fetches a message from the runner scale set message queue. If there are no messages available, it returns (nil, nil). +// Unless a message is deleted after being processed (using DeleteMessage), it will be returned again in subsequent calls. +// If the current session token is expired, it returns a MessageQueueTokenExpiredError. +// In these cases the caller should refresh the session with RefreshMessageSession. func (c *Client) GetMessage(ctx context.Context, messageQueueURL, messageQueueAccessToken string, lastMessageID uint64, maxCapacity uint32) (*RunnerScaleSetMessage, error) { u, err := url.Parse(messageQueueURL) if err != nil { @@ -604,6 +642,10 @@ func (c *Client) GetMessage(ctx context.Context, messageQueueURL, messageQueueAc return message, nil } +// DeleteMessage deletes a message from the runner scale set message queue. +// This should typically be done after processing the message and acts as an acknowledgment. +// If the current session token is expired, it returns a MessageQueueTokenExpiredError. +// In these cases the caller should refresh the session with RefreshMessageSession. func (c *Client) DeleteMessage(ctx context.Context, messageQueueURL, messageQueueAccessToken string, messageID uint64) error { u, err := url.Parse(messageQueueURL) if err != nil { @@ -651,6 +693,8 @@ func (c *Client) DeleteMessage(ctx context.Context, messageQueueURL, messageQueu } } +// CreateMessageSession creates a new message session for the specified runner scale set. +// The resulting session contains the message queue URL and access token used to GetMessage. func (c *Client) CreateMessageSession(ctx context.Context, runnerScaleSetID uint64, owner string) (*RunnerScaleSetSession, error) { path := fmt.Sprintf("/%s/%d/sessions", scaleSetEndpoint, runnerScaleSetID) @@ -672,11 +716,14 @@ func (c *Client) CreateMessageSession(ctx context.Context, runnerScaleSetID uint return createdSession, nil } +// DeleteMessageSession deletes a message session for the specified runner scale set. func (c *Client) DeleteMessageSession(ctx context.Context, runnerScaleSetID uint64, sessionID uuid.UUID) error { path := fmt.Sprintf("/%s/%d/sessions/%s", scaleSetEndpoint, runnerScaleSetID, sessionID.String()) return c.doSessionRequest(ctx, http.MethodDelete, path, nil, http.StatusNoContent, nil) } +// RefreshMessageSession refreshes a message session for the specified runner scale set. +// This should be used when a MessageQueueTokenExpiredError is encountered. func (c *Client) RefreshMessageSession(ctx context.Context, runnerScaleSetID uint64, sessionID uuid.UUID) (*RunnerScaleSetSession, error) { path := fmt.Sprintf("/%s/%d/sessions/%s", scaleSetEndpoint, runnerScaleSetID, sessionID.String()) refreshedSession := &RunnerScaleSetSession{} @@ -735,6 +782,8 @@ func (c *Client) doSessionRequest(ctx context.Context, method, path string, requ }) } +// GenerateJitRunnerConfig generates a JIT runner configuration for the specified runner scale set. This returns an encoded +// configuration that can be used to directly start a new runner. func (c *Client) GenerateJitRunnerConfig(ctx context.Context, jitRunnerSetting *RunnerScaleSetJitRunnerSetting, scaleSetID int) (*RunnerScaleSetJitRunnerConfig, error) { path := fmt.Sprintf("/%s/%d/generatejitconfig", scaleSetEndpoint, scaleSetID) @@ -769,6 +818,7 @@ func (c *Client) GenerateJitRunnerConfig(ctx context.Context, jitRunnerSetting * return runnerJitConfig, nil } +// GetRunner fetches a runner by its ID. This can be used to check if a runner exists. func (c *Client) GetRunner(ctx context.Context, runnerID int64) (*RunnerReference, error) { path := fmt.Sprintf("/%s/%d", runnerEndpoint, runnerID) @@ -799,6 +849,7 @@ func (c *Client) GetRunner(ctx context.Context, runnerID int64) (*RunnerReferenc return runnerReference, nil } +// GetRunnerByName fetches a runner by its name. This can be used to check if a runner exists. func (c *Client) GetRunnerByName(ctx context.Context, runnerName string) (*RunnerReference, error) { path := fmt.Sprintf("/%s?agentName=%s", runnerEndpoint, runnerName) @@ -841,6 +892,7 @@ func (c *Client) GetRunnerByName(ctx context.Context, runnerName string) (*Runne return &runnerList.RunnerReferences[0], nil } +// RemoveRunner removes a runner by its ID. func (c *Client) RemoveRunner(ctx context.Context, runnerID int64) error { path := fmt.Sprintf("/%s/%d", runnerEndpoint, runnerID) @@ -881,10 +933,10 @@ func (c *Client) getRunnerRegistrationToken(ctx context.Context) (*registrationT bearerToken := "" - if c.creds.Token != "" { - bearerToken = fmt.Sprintf("Bearer %v", c.creds.Token) + if c.creds.token != "" { + bearerToken = fmt.Sprintf("Bearer %v", c.creds.token) } else { - accessToken, err := c.fetchAccessToken(ctx, c.creds.App) + accessToken, err := c.fetchAccessToken(ctx, c.creds.app) if err != nil { return nil, fmt.Errorf("failed to fetch access token: %w", err) } @@ -981,19 +1033,19 @@ func (c *Client) fetchAccessToken(ctx context.Context, creds *GitHubAppAuth) (*a return accessToken, nil } -type ActionsServiceAdminConnection struct { +type actionsServiceAdminConnection struct { ActionsServiceURL *string `json:"url,omitempty"` AdminToken *string `json:"token,omitempty"` } -func (c *Client) getActionsServiceAdminConnection(ctx context.Context, rt *registrationToken) (*ActionsServiceAdminConnection, error) { +func (c *Client) getActionsServiceAdminConnection(ctx context.Context, rt *registrationToken) (*actionsServiceAdminConnection, error) { path := "/actions/runner-registration" body := struct { URL string `json:"url"` RunnerEvent string `json:"runner_event"` }{ - URL: c.config.ConfigURL.String(), + URL: c.config.configURL.String(), RunnerEvent: "register", } @@ -1059,7 +1111,7 @@ func (c *Client) getActionsServiceAdminConnection(ctx context.Context, rt *regis } } -func (c *Client) getActionsServiceAdminConnectionRequest(req *http.Request) (*ActionsServiceAdminConnection, error) { +func (c *Client) getActionsServiceAdminConnectionRequest(req *http.Request) (*actionsServiceAdminConnection, error) { resp, err := c.do(req) if err != nil { return nil, fmt.Errorf("failed to issue the request: %w", err) @@ -1067,7 +1119,7 @@ func (c *Client) getActionsServiceAdminConnectionRequest(req *http.Request) (*Ac defer resp.Body.Close() if resp.StatusCode >= 200 && resp.StatusCode <= 299 { - var actionsServiceAdminConnection *ActionsServiceAdminConnection + var actionsServiceAdminConnection *actionsServiceAdminConnection if err := json.NewDecoder(resp.Body).Decode(&actionsServiceAdminConnection); err != nil { return nil, &GitHubAPIError{ StatusCode: resp.StatusCode, @@ -1094,22 +1146,22 @@ func (c *Client) getActionsServiceAdminConnectionRequest(req *http.Request) (*Ac } } -func createRegistrationTokenPath(config *GitHubConfig) (string, error) { - switch config.Scope { - case GitHubScopeOrganization: - path := fmt.Sprintf("/orgs/%s/actions/runners/registration-token", config.Organization) +func createRegistrationTokenPath(config *gitHubConfig) (string, error) { + switch config.scope { + case gitHubScopeOrganization: + path := fmt.Sprintf("/orgs/%s/actions/runners/registration-token", config.organization) return path, nil - case GitHubScopeEnterprise: - path := fmt.Sprintf("/enterprises/%s/actions/runners/registration-token", config.Enterprise) + case gitHubScopeEnterprise: + path := fmt.Sprintf("/enterprises/%s/actions/runners/registration-token", config.enterprise) return path, nil - case GitHubScopeRepository: - path := fmt.Sprintf("/repos/%s/%s/actions/runners/registration-token", config.Organization, config.Repository) + case gitHubScopeRepository: + path := fmt.Sprintf("/repos/%s/%s/actions/runners/registration-token", config.organization, config.repository) return path, nil default: - return "", fmt.Errorf("unknown scope for config url: %s", config.ConfigURL) + return "", fmt.Errorf("unknown scope for config url: %s", config.configURL) } } @@ -1168,7 +1220,7 @@ func (c *Client) updateTokenIfNeeded(ctx context.Context) error { return nil } - c.logger.Info("refreshing token", "githubConfigUrl", c.config.ConfigURL.String()) + c.logger.Info("refreshing token", "githubConfigUrl", c.config.configURL.String()) rt, err := c.getRunnerRegistrationToken(ctx) if err != nil { return fmt.Errorf("failed to get runner registration token on refresh: %w", err) diff --git a/client_test.go b/client_test.go index 2e63288..5e99922 100644 --- a/client_test.go +++ b/client_test.go @@ -72,7 +72,7 @@ func TestNewGitHubAPIRequest(t *testing.T) { } for _, scenario := range scenarios { - client, err := NewClient(scenario.configURL, nil) + client, err := newClient(scenario.configURL, nil) require.NoError(t, err) req, err := client.newGitHubAPIRequest(ctx, http.MethodGet, scenario.path, nil) @@ -82,7 +82,7 @@ func TestNewGitHubAPIRequest(t *testing.T) { }) t.Run("sets user agent header if present", func(t *testing.T) { - client, err := NewClient("http://localhost/my-org", nil) + client, err := newClient("http://localhost/my-org", nil) require.NoError(t, err) client.SetUserAgent(testUserAgent) @@ -94,7 +94,7 @@ func TestNewGitHubAPIRequest(t *testing.T) { }) t.Run("sets the body we pass", func(t *testing.T) { - client, err := NewClient("http://localhost/my-org", nil) + client, err := newClient("http://localhost/my-org", nil) require.NoError(t, err) req, err := client.newGitHubAPIRequest( @@ -113,14 +113,14 @@ func TestNewGitHubAPIRequest(t *testing.T) { func TestNewActionsServiceRequest(t *testing.T) { ctx := context.Background() - defaultCreds := &ActionsAuth{Token: "token"} + defaultCreds := &actionsAuth{token: "token"} t.Run("manages authentication", func(t *testing.T) { t.Run("client is brand new", func(t *testing.T) { token := defaultActionsToken(t) server := testserver.New(t, nil, testserver.WithActionsToken(token)) - client, err := NewClient(server.ConfigURLForOrg("my-org"), defaultCreds) + client, err := newClient(server.ConfigURLForOrg("my-org"), defaultCreds) require.NoError(t, err) req, err := client.newActionsServiceRequest(ctx, http.MethodGet, "my-path", nil) @@ -133,7 +133,7 @@ func TestNewActionsServiceRequest(t *testing.T) { newToken := defaultActionsToken(t) server := testserver.New(t, nil, testserver.WithActionsToken(newToken)) - client, err := NewClient(server.ConfigURLForOrg("my-org"), defaultCreds) + client, err := newClient(server.ConfigURLForOrg("my-org"), defaultCreds) require.NoError(t, err) client.actionsServiceAdminToken = "expiring-token" client.actionsServiceAdminTokenExpiresAt = time.Now().Add(59 * time.Second) @@ -159,7 +159,7 @@ func TestNewActionsServiceRequest(t *testing.T) { testserver.WithActionsToken(newToken), testserver.WithActionsRegistrationTokenHandler(unauthorizedHandler), ) - client, err := NewClient(server.ConfigURLForOrg("my-org"), defaultCreds) + client, err := newClient(server.ConfigURLForOrg("my-org"), defaultCreds) require.NoError(t, err) expiringToken := "expiring-token" expiresAt := time.Now().Add(59 * time.Second) @@ -177,7 +177,7 @@ func TestNewActionsServiceRequest(t *testing.T) { errMessage := `{"message":"test"}` srv := "http://github.com/my-org" - resp := &ActionsServiceAdminConnection{ + resp := &actionsServiceAdminConnection{ AdminToken: &newToken, ActionsServiceURL: &srv, } @@ -195,7 +195,7 @@ func TestNewActionsServiceRequest(t *testing.T) { _ = json.NewEncoder(w).Encode(resp) } server := testserver.New(t, nil, testserver.WithActionsToken("random-token"), testserver.WithActionsToken(newToken), testserver.WithActionsRegistrationTokenHandler(unauthorizedHandler)) - client, err := NewClient(server.ConfigURLForOrg("my-org"), defaultCreds) + client, err := newClient(server.ConfigURLForOrg("my-org"), defaultCreds) require.NoError(t, err) expiringToken := "expiring-token" expiresAt := time.Now().Add(59 * time.Second) @@ -213,7 +213,7 @@ func TestNewActionsServiceRequest(t *testing.T) { tokenThatShouldNotBeFetched := defaultActionsToken(t) server := testserver.New(t, nil, testserver.WithActionsToken(tokenThatShouldNotBeFetched)) - client, err := NewClient(server.ConfigURLForOrg("my-org"), defaultCreds) + client, err := newClient(server.ConfigURLForOrg("my-org"), defaultCreds) require.NoError(t, err) client.actionsServiceAdminToken = "healthy-token" client.actionsServiceAdminTokenExpiresAt = time.Now().Add(1 * time.Hour) @@ -228,7 +228,7 @@ func TestNewActionsServiceRequest(t *testing.T) { t.Run("builds the right URL including api version", func(t *testing.T) { server := testserver.New(t, nil) - client, err := NewClient(server.ConfigURLForOrg("my-org"), defaultCreds) + client, err := newClient(server.ConfigURLForOrg("my-org"), defaultCreds) require.NoError(t, err) req, err := client.newActionsServiceRequest(ctx, http.MethodGet, "/my/path?name=banana", nil) @@ -247,7 +247,7 @@ func TestNewActionsServiceRequest(t *testing.T) { t.Run("populates header", func(t *testing.T) { server := testserver.New(t, nil) - client, err := NewClient(server.ConfigURLForOrg("my-org"), defaultCreds) + client, err := newClient(server.ConfigURLForOrg("my-org"), defaultCreds) require.NoError(t, err) client.SetUserAgent(testUserAgent) @@ -262,8 +262,8 @@ func TestNewActionsServiceRequest(t *testing.T) { func TestGetRunner(t *testing.T) { ctx := context.Background() - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } t.Run("Get Runner", func(t *testing.T) { @@ -278,7 +278,7 @@ func TestGetRunner(t *testing.T) { w.Write(response) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.GetRunner(ctx, runnerID) @@ -299,7 +299,7 @@ func TestGetRunner(t *testing.T) { actualRetry++ })) - client, err := NewClient(server.configURLForOrg("my-org"), auth, WithRetryMax(retryMax), WithRetryWaitMax(retryWaitMax)) + client, err := newClient(server.configURLForOrg("my-org"), auth, WithRetryMax(retryMax), WithRetryWaitMax(retryWaitMax)) require.NoError(t, err) _, err = client.GetRunner(ctx, runnerID) @@ -310,8 +310,8 @@ func TestGetRunner(t *testing.T) { func TestGetRunnerByName(t *testing.T) { ctx := context.Background() - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } t.Run("Get Runner by Name", func(t *testing.T) { @@ -327,7 +327,7 @@ func TestGetRunnerByName(t *testing.T) { w.Write(response) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.GetRunnerByName(ctx, runnerName) @@ -343,7 +343,7 @@ func TestGetRunnerByName(t *testing.T) { w.Write(response) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.GetRunnerByName(ctx, runnerName) @@ -365,7 +365,7 @@ func TestGetRunnerByName(t *testing.T) { actualRetry++ })) - client, err := NewClient(server.configURLForOrg("my-org"), auth, WithRetryMax(retryMax), WithRetryWaitMax(retryWaitMax)) + client, err := newClient(server.configURLForOrg("my-org"), auth, WithRetryMax(retryMax), WithRetryWaitMax(retryWaitMax)) require.NoError(t, err) _, err = client.GetRunnerByName(ctx, runnerName) @@ -376,8 +376,8 @@ func TestGetRunnerByName(t *testing.T) { func TestDeleteRunner(t *testing.T) { ctx := context.Background() - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } t.Run("Delete Runner", func(t *testing.T) { @@ -387,7 +387,7 @@ func TestDeleteRunner(t *testing.T) { w.WriteHeader(http.StatusNoContent) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) err = client.RemoveRunner(ctx, runnerID) @@ -408,7 +408,7 @@ func TestDeleteRunner(t *testing.T) { actualRetry++ })) - client, err := NewClient( + client, err := newClient( server.configURLForOrg("my-org"), auth, WithRetryMax(retryMax), @@ -424,8 +424,8 @@ func TestDeleteRunner(t *testing.T) { func TestGetRunnerGroupByName(t *testing.T) { ctx := context.Background() - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } t.Run("Get RunnerGroup by Name", func(t *testing.T) { @@ -441,7 +441,7 @@ func TestGetRunnerGroupByName(t *testing.T) { w.Write(response) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.GetRunnerGroupByName(ctx, runnerGroupName) @@ -457,7 +457,7 @@ func TestGetRunnerGroupByName(t *testing.T) { w.Write(response) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.GetRunnerGroupByName(ctx, runnerGroupName) @@ -468,8 +468,8 @@ func TestGetRunnerGroupByName(t *testing.T) { func TestGetRunnerScaleSet(t *testing.T) { ctx := context.Background() - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } scaleSetName := "ScaleSet" @@ -482,7 +482,7 @@ func TestGetRunnerScaleSet(t *testing.T) { w.Write(runnerScaleSetsResp) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.GetRunnerScaleSet(ctx, 1, scaleSetName) @@ -498,7 +498,7 @@ func TestGetRunnerScaleSet(t *testing.T) { url = *r.URL })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.GetRunnerScaleSet(ctx, 1, scaleSetName) @@ -515,7 +515,7 @@ func TestGetRunnerScaleSet(t *testing.T) { w.WriteHeader(http.StatusNotFound) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.GetRunnerScaleSet(ctx, 1, scaleSetName) @@ -528,7 +528,7 @@ func TestGetRunnerScaleSet(t *testing.T) { w.Header().Set("Content-Type", "text/plain") })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.GetRunnerScaleSet(ctx, 1, scaleSetName) @@ -545,7 +545,7 @@ func TestGetRunnerScaleSet(t *testing.T) { retryMax := 1 retryWaitMax := 1 * time.Microsecond - client, err := NewClient( + client, err := newClient( server.configURLForOrg("my-org"), auth, WithRetryMax(retryMax), @@ -566,7 +566,7 @@ func TestGetRunnerScaleSet(t *testing.T) { w.Write(runnerScaleSetsResp) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.GetRunnerScaleSet(ctx, 1, scaleSetName) @@ -587,7 +587,7 @@ func TestGetRunnerScaleSet(t *testing.T) { w.Write(runnerScaleSetsResp) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.GetRunnerScaleSet(ctx, 1, scaleSetName) @@ -598,8 +598,8 @@ func TestGetRunnerScaleSet(t *testing.T) { func TestGetRunnerScaleSetByID(t *testing.T) { ctx := context.Background() - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } scaleSetCreationDateTime := time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC) @@ -613,7 +613,7 @@ func TestGetRunnerScaleSetByID(t *testing.T) { w.Write(rsl) })) - client, err := NewClient(sservere.configURLForOrg("my-org"), auth) + client, err := newClient(sservere.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.GetRunnerScaleSetByID(ctx, runnerScaleSet.ID) @@ -631,7 +631,7 @@ func TestGetRunnerScaleSetByID(t *testing.T) { url = *r.URL })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.GetRunnerScaleSetByID(ctx, runnerScaleSet.ID) @@ -647,7 +647,7 @@ func TestGetRunnerScaleSetByID(t *testing.T) { w.WriteHeader(http.StatusNotFound) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.GetRunnerScaleSetByID(ctx, runnerScaleSet.ID) @@ -660,7 +660,7 @@ func TestGetRunnerScaleSetByID(t *testing.T) { w.Header().Set("Content-Type", "text/plain") })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.GetRunnerScaleSetByID(ctx, runnerScaleSet.ID) @@ -676,7 +676,7 @@ func TestGetRunnerScaleSetByID(t *testing.T) { retryMax := 1 retryWaitMax := 1 * time.Microsecond - client, err := NewClient( + client, err := newClient( server.configURLForOrg("my-org"), auth, WithRetryMax(retryMax), @@ -698,7 +698,7 @@ func TestGetRunnerScaleSetByID(t *testing.T) { w.Write(rsl) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.GetRunnerScaleSetByID(ctx, runnerScaleSet.ID) @@ -709,8 +709,8 @@ func TestGetRunnerScaleSetByID(t *testing.T) { func TestCreateRunnerScaleSet(t *testing.T) { ctx := context.Background() - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } scaleSetCreationDateTime := time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC) @@ -724,7 +724,7 @@ func TestCreateRunnerScaleSet(t *testing.T) { w.Write(rsl) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.CreateRunnerScaleSet(ctx, &runnerScaleSet) @@ -741,7 +741,7 @@ func TestCreateRunnerScaleSet(t *testing.T) { url = *r.URL })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.CreateRunnerScaleSet(ctx, &runnerScaleSet) @@ -758,7 +758,7 @@ func TestCreateRunnerScaleSet(t *testing.T) { w.Header().Set("Content-Type", "text/plain") })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.CreateRunnerScaleSet(ctx, &runnerScaleSet) @@ -777,7 +777,7 @@ func TestCreateRunnerScaleSet(t *testing.T) { retryMax := 1 retryWaitMax := 1 * time.Microsecond - client, err := NewClient( + client, err := newClient( server.configURLForOrg("my-org"), auth, WithRetryMax(retryMax), @@ -794,8 +794,8 @@ func TestCreateRunnerScaleSet(t *testing.T) { func TestUpdateRunnerScaleSet(t *testing.T) { ctx := context.Background() - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } scaleSetCreationDateTime := time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC) @@ -809,7 +809,7 @@ func TestUpdateRunnerScaleSet(t *testing.T) { w.Write(rsl) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.UpdateRunnerScaleSet(ctx, 1, &RunnerScaleSet{RunnerGroupID: 1}) @@ -829,7 +829,7 @@ func TestUpdateRunnerScaleSet(t *testing.T) { w.Write(rsl) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.UpdateRunnerScaleSet(ctx, 1, &runnerScaleSet) @@ -839,8 +839,8 @@ func TestUpdateRunnerScaleSet(t *testing.T) { func TestDeleteRunnerScaleSet(t *testing.T) { ctx := context.Background() - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } t.Run("Delete runner scale set", func(t *testing.T) { @@ -850,7 +850,7 @@ func TestDeleteRunnerScaleSet(t *testing.T) { w.WriteHeader(http.StatusNoContent) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) err = client.DeleteRunnerScaleSet(ctx, 10) @@ -865,7 +865,7 @@ func TestDeleteRunnerScaleSet(t *testing.T) { w.Write([]byte(`{"message": "test error"}`)) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) err = client.DeleteRunnerScaleSet(ctx, 10) @@ -875,8 +875,8 @@ func TestDeleteRunnerScaleSet(t *testing.T) { func TestCreateMessageSession(t *testing.T) { ctx := context.Background() - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } t.Run("CreateMessageSession unmarshals correctly", func(t *testing.T) { @@ -911,7 +911,7 @@ func TestCreateMessageSession(t *testing.T) { w.Write(resp) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.CreateMessageSession(ctx, runnerScaleSet.ID, owner) @@ -945,7 +945,7 @@ func TestCreateMessageSession(t *testing.T) { w.Write(resp) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.CreateMessageSession(ctx, runnerScaleSet.ID, owner) @@ -982,7 +982,7 @@ func TestCreateMessageSession(t *testing.T) { wantRetries := retryMax + 1 - client, err := NewClient( + client, err := newClient( server.configURLForOrg("my-org"), auth, WithRetryMax(retryMax), @@ -998,8 +998,8 @@ func TestCreateMessageSession(t *testing.T) { func TestDeleteMessageSession(t *testing.T) { ctx := context.Background() - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } t.Run("DeleteMessageSession call is retried the correct amount of times", func(t *testing.T) { @@ -1021,7 +1021,7 @@ func TestDeleteMessageSession(t *testing.T) { wantRetries := retryMax + 1 - client, err := NewClient( + client, err := newClient( server.configURLForOrg("my-org"), auth, WithRetryMax(retryMax), @@ -1038,8 +1038,8 @@ func TestDeleteMessageSession(t *testing.T) { } func TestRefreshMessageSession(t *testing.T) { - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } t.Run("RefreshMessageSession call is retried the correct amount of times", func(t *testing.T) { @@ -1061,7 +1061,7 @@ func TestRefreshMessageSession(t *testing.T) { wantRetries := retryMax + 1 - client, err := NewClient( + client, err := newClient( server.configURLForOrg("my-org"), auth, WithRetryMax(retryMax), @@ -1079,8 +1079,8 @@ func TestRefreshMessageSession(t *testing.T) { func TestGetMessage(t *testing.T) { ctx := context.Background() - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjI1MTYyMzkwMjJ9.tlrHslTmDkoqnc4Kk9ISoKoUNDfHo-kjlH-ByISBqzE" @@ -1096,7 +1096,7 @@ func TestGetMessage(t *testing.T) { w.Write(response) })) - client, err := NewClient(s.configURLForOrg("my-org"), auth) + client, err := newClient(s.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.GetMessage(ctx, s.URL, token, 0, 10) @@ -1113,7 +1113,7 @@ func TestGetMessage(t *testing.T) { w.Write(response) })) - client, err := NewClient(s.configURLForOrg("my-org"), auth) + client, err := newClient(s.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.GetMessage(ctx, s.URL, token, 1, 10) @@ -1132,7 +1132,7 @@ func TestGetMessage(t *testing.T) { actualRetry++ })) - client, err := NewClient( + client, err := newClient( server.configURLForOrg("my-org"), auth, WithRetryMax(retryMax), @@ -1150,7 +1150,7 @@ func TestGetMessage(t *testing.T) { w.WriteHeader(http.StatusUnauthorized) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.GetMessage(ctx, server.URL, token, 0, 10) @@ -1169,7 +1169,7 @@ func TestGetMessage(t *testing.T) { w.WriteHeader(http.StatusNotFound) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.GetMessage(ctx, server.URL, token, 0, 10) @@ -1183,7 +1183,7 @@ func TestGetMessage(t *testing.T) { w.Header().Set("Content-Type", "text/plain") })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.GetMessage(ctx, server.URL, token, 0, 10) @@ -1201,7 +1201,7 @@ func TestGetMessage(t *testing.T) { w.Header().Set("Content-Type", "text/plain") })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.GetMessage(ctx, server.URL, token, 0, 0) @@ -1214,8 +1214,8 @@ func TestGetMessage(t *testing.T) { func TestDeleteMessage(t *testing.T) { ctx := context.Background() - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjI1MTYyMzkwMjJ9.tlrHslTmDkoqnc4Kk9ISoKoUNDfHo-kjlH-ByISBqzE" @@ -1229,7 +1229,7 @@ func TestDeleteMessage(t *testing.T) { w.WriteHeader(http.StatusNoContent) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageID) @@ -1241,7 +1241,7 @@ func TestDeleteMessage(t *testing.T) { w.WriteHeader(http.StatusUnauthorized) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) err = client.DeleteMessage(ctx, server.URL, token, 0) @@ -1256,7 +1256,7 @@ func TestDeleteMessage(t *testing.T) { w.Header().Set("Content-Type", "text/plain") })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageID) @@ -1274,7 +1274,7 @@ func TestDeleteMessage(t *testing.T) { })) retryMax := 1 - client, err := NewClient( + client, err := newClient( server.configURLForOrg("my-org"), auth, WithRetryMax(retryMax), @@ -1296,7 +1296,7 @@ func TestDeleteMessage(t *testing.T) { w.Write(rsl) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageID+1) @@ -1319,7 +1319,7 @@ func TestClientProxy(t *testing.T) { return proxyConfig.ProxyFunc()(req.URL) } - c, err := NewClient("http://github.com/org/repo", nil, WithProxy(proxyFunc)) + c, err := newClient("http://github.com/org/repo", nil, WithProxy(proxyFunc)) require.NoError(t, err) req, err := http.NewRequest(http.MethodGet, "http://example.com", nil) @@ -1333,8 +1333,8 @@ func TestClientProxy(t *testing.T) { func TestGenerateJitRunnerConfig(t *testing.T) { ctx := context.Background() - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } t.Run("Get JIT Config for Runner", func(t *testing.T) { @@ -1346,7 +1346,7 @@ func TestGenerateJitRunnerConfig(t *testing.T) { server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Write(response) })) - client, err := NewClient(server.configURLForOrg("my-org"), auth) + client, err := newClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.GenerateJitRunnerConfig(ctx, runnerSettings, 1) @@ -1366,7 +1366,7 @@ func TestGenerateJitRunnerConfig(t *testing.T) { actualRetry++ })) - client, err := NewClient( + client, err := newClient( server.configURLForOrg("my-org"), auth, WithRetryMax(1), @@ -1387,7 +1387,7 @@ func TestClient_Do(t *testing.T) { })) defer server.Close() - client, err := NewClient("https://localhost/org/repo", &ActionsAuth{Token: "token"}) + client, err := newClient("https://localhost/org/repo", &actionsAuth{token: "token"}) require.NoError(t, err) req, err := http.NewRequest("GET", server.URL, nil) @@ -1413,7 +1413,7 @@ func TestClient_Do(t *testing.T) { })) defer server.Close() - client, err := NewClient("https://localhost/org/repo", &ActionsAuth{Token: "token"}) + client, err := newClient("https://localhost/org/repo", &actionsAuth{token: "token"}) require.NoError(t, err) req, err := http.NewRequest("GET", server.URL, nil) @@ -1543,10 +1543,10 @@ func TestServerWithSelfSignedCertificates(t *testing.T) { u = server.URL configURL := server.URL + "/my-org" - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } - client, err := NewClient(configURL, auth) + client, err := newClient(configURL, auth) require.NoError(t, err) require.NotNil(t, client) @@ -1574,8 +1574,8 @@ func TestServerWithSelfSignedCertificates(t *testing.T) { u = server.URL configURL := server.URL + "/my-org" - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } cert, err := os.ReadFile(filepath.Join("testdata", "rootCA.crt")) @@ -1584,7 +1584,7 @@ func TestServerWithSelfSignedCertificates(t *testing.T) { pool := x509.NewCertPool() require.True(t, pool.AppendCertsFromPEM(cert)) - client, err := NewClient( + client, err := newClient( configURL, auth, WithRootCAs(pool), @@ -1606,8 +1606,8 @@ func TestServerWithSelfSignedCertificates(t *testing.T) { u = server.URL configURL := server.URL + "/my-org" - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } cert, err := os.ReadFile(filepath.Join("testdata", "intermediate.crt")) @@ -1616,7 +1616,7 @@ func TestServerWithSelfSignedCertificates(t *testing.T) { pool := x509.NewCertPool() require.True(t, pool.AppendCertsFromPEM(cert)) - client, err := NewClient( + client, err := newClient( configURL, auth, WithRootCAs(pool), @@ -1633,11 +1633,11 @@ func TestServerWithSelfSignedCertificates(t *testing.T) { server := startNewTLSTestServer(t, certPath, keyPath, http.HandlerFunc(h)) configURL := server.URL + "/my-org" - auth := &ActionsAuth{ - Token: "token", + auth := &actionsAuth{ + token: "token", } - client, err := NewClient(configURL, auth, WithoutTLSVerify()) + client, err := newClient(configURL, auth, WithoutTLSVerify()) require.NoError(t, err) assert.NotNil(t, client) }) diff --git a/config.go b/config.go index 508e174..fcf549d 100644 --- a/config.go +++ b/config.go @@ -9,27 +9,27 @@ import ( var ErrInvalidGitHubConfigURL = fmt.Errorf("invalid config URL, should point to an enterprise, org, or repository") -type GitHubScope int +type gitHubScope int const ( - GitHubScopeUnknown GitHubScope = iota - GitHubScopeEnterprise - GitHubScopeOrganization - GitHubScopeRepository + gitHubScopeUnknown gitHubScope = iota + gitHubScopeEnterprise + gitHubScopeOrganization + gitHubScopeRepository ) -type GitHubConfig struct { - ConfigURL *url.URL - Scope GitHubScope +type gitHubConfig struct { + configURL *url.URL + scope gitHubScope - Enterprise string - Organization string - Repository string + enterprise string + organization string + repository string - IsHosted bool + isHosted bool } -func ParseGitHubConfigFromURL(in string) (*GitHubConfig, error) { +func parseGitHubConfigFromURL(in string) (*gitHubConfig, error) { u, err := url.Parse(strings.Trim(in, "/")) if err != nil { return nil, fmt.Errorf("failed to parse URL: %w", err) @@ -37,9 +37,9 @@ func ParseGitHubConfigFromURL(in string) (*GitHubConfig, error) { isHosted := isHostedGitHubURL(u) - configURL := &GitHubConfig{ - ConfigURL: u, - IsHosted: isHosted, + configURL := &gitHubConfig{ + configURL: u, + isHosted: isHosted, } invalidURLError := fmt.Errorf("%q: %w", u.String(), ErrInvalidGitHubConfigURL) @@ -52,19 +52,19 @@ func ParseGitHubConfigFromURL(in string) (*GitHubConfig, error) { return nil, invalidURLError } - configURL.Scope = GitHubScopeOrganization - configURL.Organization = pathParts[0] + configURL.scope = gitHubScopeOrganization + configURL.organization = pathParts[0] case 2: // Repository or enterprise if strings.ToLower(pathParts[0]) == "enterprises" { - configURL.Scope = GitHubScopeEnterprise - configURL.Enterprise = pathParts[1] + configURL.scope = gitHubScopeEnterprise + configURL.enterprise = pathParts[1] break } - configURL.Scope = GitHubScopeRepository - configURL.Organization = pathParts[0] - configURL.Repository = pathParts[1] + configURL.scope = gitHubScopeRepository + configURL.organization = pathParts[0] + configURL.repository = pathParts[1] default: return nil, invalidURLError } @@ -72,20 +72,20 @@ func ParseGitHubConfigFromURL(in string) (*GitHubConfig, error) { return configURL, nil } -func (c *GitHubConfig) GitHubAPIURL(path string) *url.URL { +func (c *gitHubConfig) gitHubAPIURL(path string) *url.URL { result := &url.URL{ - Scheme: c.ConfigURL.Scheme, - Host: c.ConfigURL.Host, // default for Enterprise mode + Scheme: c.configURL.Scheme, + Host: c.configURL.Host, // default for Enterprise mode Path: "/api/v3", // default for Enterprise mode } - isHosted := isHostedGitHubURL(c.ConfigURL) + isHosted := isHostedGitHubURL(c.configURL) if isHosted { - result.Host = fmt.Sprintf("api.%s", c.ConfigURL.Host) + result.Host = fmt.Sprintf("api.%s", c.configURL.Host) result.Path = "" - if strings.EqualFold("www.github.com", c.ConfigURL.Host) { + if strings.EqualFold("www.github.com", c.configURL.Host) { // re-routing www.github.com to api.github.com result.Host = "api.github.com" } diff --git a/config_test.go b/config_test.go index 3f6c2ff..bbdb015 100644 --- a/config_test.go +++ b/config_test.go @@ -15,116 +15,116 @@ func TestGitHubConfig(t *testing.T) { t.Run("when given a valid URL", func(t *testing.T) { tests := []struct { configURL string - expected *GitHubConfig + expected *gitHubConfig }{ { configURL: "https://github.com/org/repo", - expected: &GitHubConfig{ - Scope: GitHubScopeRepository, - Enterprise: "", - Organization: "org", - Repository: "repo", - IsHosted: true, + expected: &gitHubConfig{ + scope: gitHubScopeRepository, + enterprise: "", + organization: "org", + repository: "repo", + isHosted: true, }, }, { configURL: "https://github.com/org/repo/", - expected: &GitHubConfig{ - Scope: GitHubScopeRepository, - Enterprise: "", - Organization: "org", - Repository: "repo", - IsHosted: true, + expected: &gitHubConfig{ + scope: gitHubScopeRepository, + enterprise: "", + organization: "org", + repository: "repo", + isHosted: true, }, }, { configURL: "https://github.com/org", - expected: &GitHubConfig{ - Scope: GitHubScopeOrganization, - Enterprise: "", - Organization: "org", - Repository: "", - IsHosted: true, + expected: &gitHubConfig{ + scope: gitHubScopeOrganization, + enterprise: "", + organization: "org", + repository: "", + isHosted: true, }, }, { configURL: "https://github.com/enterprises/my-enterprise", - expected: &GitHubConfig{ - Scope: GitHubScopeEnterprise, - Enterprise: "my-enterprise", - Organization: "", - Repository: "", - IsHosted: true, + expected: &gitHubConfig{ + scope: gitHubScopeEnterprise, + enterprise: "my-enterprise", + organization: "", + repository: "", + isHosted: true, }, }, { configURL: "https://github.com/enterprises/my-enterprise/", - expected: &GitHubConfig{ - Scope: GitHubScopeEnterprise, - Enterprise: "my-enterprise", - Organization: "", - Repository: "", - IsHosted: true, + expected: &gitHubConfig{ + scope: gitHubScopeEnterprise, + enterprise: "my-enterprise", + organization: "", + repository: "", + isHosted: true, }, }, { configURL: "https://www.github.com/org", - expected: &GitHubConfig{ - Scope: GitHubScopeOrganization, - Enterprise: "", - Organization: "org", - Repository: "", - IsHosted: true, + expected: &gitHubConfig{ + scope: gitHubScopeOrganization, + enterprise: "", + organization: "org", + repository: "", + isHosted: true, }, }, { configURL: "https://www.github.com/org/", - expected: &GitHubConfig{ - Scope: GitHubScopeOrganization, - Enterprise: "", - Organization: "org", - Repository: "", - IsHosted: true, + expected: &gitHubConfig{ + scope: gitHubScopeOrganization, + enterprise: "", + organization: "org", + repository: "", + isHosted: true, }, }, { configURL: "https://github.localhost/org", - expected: &GitHubConfig{ - Scope: GitHubScopeOrganization, - Enterprise: "", - Organization: "org", - Repository: "", - IsHosted: true, + expected: &gitHubConfig{ + scope: gitHubScopeOrganization, + enterprise: "", + organization: "org", + repository: "", + isHosted: true, }, }, { configURL: "https://my-ghes.com/org", - expected: &GitHubConfig{ - Scope: GitHubScopeOrganization, - Enterprise: "", - Organization: "org", - Repository: "", - IsHosted: false, + expected: &gitHubConfig{ + scope: gitHubScopeOrganization, + enterprise: "", + organization: "org", + repository: "", + isHosted: false, }, }, { configURL: "https://my-ghes.com/org/", - expected: &GitHubConfig{ - Scope: GitHubScopeOrganization, - Enterprise: "", - Organization: "org", - Repository: "", - IsHosted: false, + expected: &gitHubConfig{ + scope: gitHubScopeOrganization, + enterprise: "", + organization: "org", + repository: "", + isHosted: false, }, }, { configURL: "https://my-ghes.ghe.com/org/", - expected: &GitHubConfig{ - Scope: GitHubScopeOrganization, - Enterprise: "", - Organization: "org", - Repository: "", - IsHosted: true, + expected: &gitHubConfig{ + scope: gitHubScopeOrganization, + enterprise: "", + organization: "org", + repository: "", + isHosted: true, }, }, } @@ -133,9 +133,9 @@ func TestGitHubConfig(t *testing.T) { t.Run(test.configURL, func(t *testing.T) { parsedURL, err := url.Parse(strings.Trim(test.configURL, "/")) require.NoError(t, err) - test.expected.ConfigURL = parsedURL + test.expected.configURL = parsedURL - cfg, err := ParseGitHubConfigFromURL(test.configURL) + cfg, err := parseGitHubConfigFromURL(test.configURL) require.NoError(t, err) assert.Equal(t, test.expected, cfg) }) @@ -150,7 +150,7 @@ func TestGitHubConfig(t *testing.T) { } for _, u := range invalidURLs { - _, err := ParseGitHubConfigFromURL(u) + _, err := parseGitHubConfigFromURL(u) require.Error(t, err) assert.True(t, errors.Is(err, ErrInvalidGitHubConfigURL)) } @@ -159,37 +159,37 @@ func TestGitHubConfig(t *testing.T) { func TestGitHubConfig_GitHubAPIURL(t *testing.T) { t.Run("when hosted", func(t *testing.T) { - config, err := ParseGitHubConfigFromURL("https://github.com/org/repo") + config, err := parseGitHubConfigFromURL("https://github.com/org/repo") require.NoError(t, err) - assert.True(t, config.IsHosted) + assert.True(t, config.isHosted) - result := config.GitHubAPIURL("/some/path") + result := config.gitHubAPIURL("/some/path") assert.Equal(t, "https://api.github.com/some/path", result.String()) }) t.Run("when hosted with ghe.com", func(t *testing.T) { - config, err := ParseGitHubConfigFromURL("https://github.ghe.com/org/repo") + config, err := parseGitHubConfigFromURL("https://github.ghe.com/org/repo") require.NoError(t, err) - assert.True(t, config.IsHosted) + assert.True(t, config.isHosted) - result := config.GitHubAPIURL("/some/path") + result := config.gitHubAPIURL("/some/path") assert.Equal(t, "https://api.github.ghe.com/some/path", result.String()) }) t.Run("when not hosted", func(t *testing.T) { - config, err := ParseGitHubConfigFromURL("https://ghes.com/org/repo") + config, err := parseGitHubConfigFromURL("https://ghes.com/org/repo") require.NoError(t, err) - assert.False(t, config.IsHosted) + assert.False(t, config.isHosted) - result := config.GitHubAPIURL("/some/path") + result := config.gitHubAPIURL("/some/path") assert.Equal(t, "https://ghes.com/api/v3/some/path", result.String()) }) t.Run("when not hosted with ghe.com", func(t *testing.T) { os.Setenv("GITHUB_ACTIONS_FORCE_GHES", "1") defer os.Unsetenv("GITHUB_ACTIONS_FORCE_GHES") - config, err := ParseGitHubConfigFromURL("https://test.ghe.com/org/repo") + config, err := parseGitHubConfigFromURL("https://test.ghe.com/org/repo") require.NoError(t, err) - assert.False(t, config.IsHosted) + assert.False(t, config.isHosted) - result := config.GitHubAPIURL("/some/path") + result := config.gitHubAPIURL("/some/path") assert.Equal(t, "https://test.ghe.com/api/v3/some/path", result.String()) }) } diff --git a/listener/listener.go b/listener/listener.go index 3555612..972465a 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -20,6 +20,7 @@ const ( sessionCreationMaxRetries = 10 ) +// Config holds the configuration for the Listener. type Config struct { ScaleSetID uint64 MinRunners uint32 @@ -33,6 +34,7 @@ func (c *Config) defaults() { } } +// Validate returns an error if the configuration is invalid. func (c *Config) Validate() error { c.defaults() @@ -45,6 +47,8 @@ func (c *Config) Validate() error { return nil } +// Listener listens for messages from the scaleset service and handles them. It automatically handles session +// creation/deletion/refreshing and message polling and acking. type Listener struct { // The main client responsible for communicating with the scaleset service client *scaleset.Client @@ -68,6 +72,7 @@ func (l *Listener) SetMaxRunners(count uint32) { l.maxRunners.Store(count) } +// New creates a new Listener with the given configuration. func New(client *scaleset.Client, config Config) (*Listener, error) { if client == nil { return nil, errors.New("client is required") @@ -94,13 +99,15 @@ func New(client *scaleset.Client, config Config) (*Listener, error) { return listener, nil } +// Scaler defines the interface for handling scale set messages. type Scaler interface { HandleJobStarted(ctx context.Context, jobInfo *scaleset.JobStarted) error HandleJobCompleted(ctx context.Context, jobInfo *scaleset.JobCompleted) error HandleDesiredRunnerCount(ctx context.Context, count uint64) (int, error) } -func (l *Listener) Run(ctx context.Context, handler Scaler) error { +// Run starts the listener and processes messages using the provided scaler. +func (l *Listener) Run(ctx context.Context, scaler Scaler) error { l.logger.Info("Creating message session") if err := l.createSession(ctx); err != nil { return fmt.Errorf("failed to create session: %w", err) @@ -120,7 +127,7 @@ func (l *Listener) Run(ctx context.Context, handler Scaler) error { l.logger.Info("Message session created; listening for messages", "sessionID", l.session.SessionID) // Handle initial statistics - if _, err := handler.HandleDesiredRunnerCount(ctx, l.session.Statistics.TotalAssignedJobs); err != nil { + if _, err := scaler.HandleDesiredRunnerCount(ctx, l.session.Statistics.TotalAssignedJobs); err != nil { return fmt.Errorf("handling initial message failed: %w", err) } @@ -137,7 +144,7 @@ func (l *Listener) Run(ctx context.Context, handler Scaler) error { } if msg == nil { - _, err := handler.HandleDesiredRunnerCount(ctx, 0) + _, err := scaler.HandleDesiredRunnerCount(ctx, 0) if err != nil { return fmt.Errorf("handling nil message failed: %w", err) } @@ -146,7 +153,7 @@ func (l *Listener) Run(ctx context.Context, handler Scaler) error { } // Remove cancellation from the context to avoid cancelling the message handling. - if err := l.handleMessage(context.WithoutCancel(ctx), handler, msg); err != nil { + if err := l.handleMessage(context.WithoutCancel(ctx), scaler, msg); err != nil { return fmt.Errorf("failed to handle message: %w", err) } }