From b1dcbc44e6575d6a5bfefc587f8501bd0cbe88cd Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Thu, 30 Oct 2025 14:24:07 +0100 Subject: [PATCH] Renames, style changes and unexports (#3) --- actions_server_test.go | 2 +- byte_order_mark_test.go | 11 +- client.go | 585 ++++++++++++------------ client_generate_jit_test.go | 19 +- client_job_acquisition_test.go | 57 ++- client_proxy_test.go | 7 +- client_runner_scale_set_message_test.go | 73 ++- client_runner_scale_set_session_test.go | 93 ++-- client_runner_scale_set_test.go | 105 +++-- client_runner_test.go | 43 +- client_tls_test.go | 25 +- config_test.go | 63 ++- errors.go => error.go | 17 +- errors_test.go | 61 ++- github_api_request_test.go | 77 ++-- types.go | 24 +- user_agent_test.go | 6 +- 17 files changed, 633 insertions(+), 635 deletions(-) rename errors.go => error.go (83%) diff --git a/actions_server_test.go b/actions_server_test.go index e13de85..3d92180 100644 --- a/actions_server_test.go +++ b/actions_server_test.go @@ -1,4 +1,4 @@ -package scaleset_test +package scaleset import ( "net/http" diff --git a/byte_order_mark_test.go b/byte_order_mark_test.go index 81a6cf2..0615586 100644 --- a/byte_order_mark_test.go +++ b/byte_order_mark_test.go @@ -1,4 +1,4 @@ -package scaleset_test +package scaleset import ( "io" @@ -6,7 +6,6 @@ import ( "net/http/httptest" "testing" - "github.com/actions/scaleset" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -18,13 +17,13 @@ func TestClient_Do(t *testing.T) { })) defer server.Close() - client, err := scaleset.NewClient("https://localhost/org/repo", &scaleset.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) require.NoError(t, err) - resp, err := client.Do(req) + resp, err := client.do(req) require.NoError(t, err) body, err := io.ReadAll(resp.Body) @@ -44,13 +43,13 @@ func TestClient_Do(t *testing.T) { })) defer server.Close() - client, err := scaleset.NewClient("https://localhost/org/repo", &scaleset.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) require.NoError(t, err) - resp, err := client.Do(req) + resp, err := client.do(req) require.NoError(t, err) body, err := io.ReadAll(resp.Body) diff --git a/client.go b/client.go index 58d383f..35d18cb 100644 --- a/client.go +++ b/client.go @@ -1,25 +1,25 @@ -// Package scaleset provides a client for GitHub Actions Runner Scale Set API. +// Package scaleset package provides a client to interact with GitHub Scale Set APIs. package scaleset import ( "bytes" "context" - "crypto/sha256" "crypto/tls" "crypto/x509" "encoding/json" "errors" "fmt" "io" + "log/slog" "maps" "math/rand" "net/http" "net/url" "strconv" "sync" + "sync/atomic" "time" - "github.com/go-logr/logr" "github.com/golang-jwt/jwt/v4" "github.com/google/uuid" "github.com/hashicorp/go-retryablehttp" @@ -30,27 +30,45 @@ const ( scaleSetEndpoint = "_apis/runtime/runnerscalesets" ) -// Header used to propagate capacity information to the back-end +type atomicValue[T any] struct { + v atomic.Value +} + +func (v *atomicValue[T]) CompareAndSwap(old, new T) (swapped bool) { + return v.v.CompareAndSwap(old, new) +} + +func (v *atomicValue[T]) Load() T { + return v.v.Load().(T) +} + +func (v *atomicValue[T]) Store(val T) { + v.v.Store(val) +} + +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 const HeaderScaleSetMaxCapacity = "X-ScaleSetMaxCapacity" type Client struct { - *http.Client + httpClient *http.Client - // lock for refreshing the ActionsServiceAdminToken and ActionsServiceAdminTokenExpiresAt - mu sync.Mutex - - // TODO: Convert to unexported fields once refactor of Listener is complete - ActionsServiceAdminToken string - ActionsServiceAdminTokenExpiresAt time.Time - ActionsServiceURL string + actionsMu sync.Mutex // guards actionsService fields + actionsServiceAdminToken string + actionsServiceAdminTokenExpiresAt time.Time + actionsServiceURL string retryMax int retryWaitMax time.Duration - creds *ActionsAuth - config *GitHubConfig - logger logr.Logger - userAgent UserAgentInfo + creds *ActionsAuth + config *GitHubConfig + logger *slog.Logger + + userAgent atomicValue[string] rootCAs *x509.CertPool tlsInsecureSkipVerify bool @@ -60,24 +78,27 @@ type Client struct { type GitHubAppAuth struct { // AppID is the ID or the Client ID of the application - AppID string + AppID string + // AppInstallationID is the installation ID of the GitHub App AppInstallationID int64 - AppPrivateKey string + // AppPrivateKey is the private key of the GitHub App in PEM format + AppPrivateKey string } type ActionsAuth struct { - // GitHub App - AppCreds *GitHubAppAuth - + // AppCreds is the GitHub App credentials + App *GitHubAppAuth // GitHub PAT Token string } type ProxyFunc func(req *http.Request) (*url.URL, error) -type ClientOption func(*Client) +type Option func(*Client) type UserAgentInfo struct { + // System is the name of the scale set implementation + System string // Version is the version of the controller Version string // CommitSHA is the git commit SHA of the controller @@ -102,46 +123,46 @@ func (u UserAgentInfo) String() string { proxy = "Proxy/enabled" } - return fmt.Sprintf("actions-runner-controller/%s (%s; %s) ScaleSetID/%s (%s)", u.Version, u.CommitSHA, u.Subsystem, scaleSetID, proxy) + return fmt.Sprintf("%s/%s (%s; %s) ScaleSetID/%s (%s)", u.System, u.Version, u.CommitSHA, u.Subsystem, scaleSetID, proxy) } -func WithLogger(logger logr.Logger) ClientOption { +func WithLogger(logger slog.Logger) Option { return func(c *Client) { - c.logger = logger + c.logger = &logger } } -func WithRetryMax(retryMax int) ClientOption { +func WithRetryMax(retryMax int) Option { return func(c *Client) { c.retryMax = retryMax } } -func WithRetryWaitMax(retryWaitMax time.Duration) ClientOption { +func WithRetryWaitMax(retryWaitMax time.Duration) Option { return func(c *Client) { c.retryWaitMax = retryWaitMax } } -func WithRootCAs(rootCAs *x509.CertPool) ClientOption { +func WithRootCAs(rootCAs *x509.CertPool) Option { return func(c *Client) { c.rootCAs = rootCAs } } -func WithoutTLSVerify() ClientOption { +func WithoutTLSVerify() Option { return func(c *Client) { c.tlsInsecureSkipVerify = true } } -func WithProxy(proxyFunc ProxyFunc) ClientOption { +func WithProxy(proxyFunc ProxyFunc) Option { return func(c *Client) { c.proxyFunc = proxyFunc } } -func NewClient(githubConfigURL string, creds *ActionsAuth, options ...ClientOption) (*Client, error) { +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) @@ -150,23 +171,26 @@ func NewClient(githubConfigURL string, creds *ActionsAuth, options ...ClientOpti ac := &Client{ creds: creds, config: config, - logger: logr.Discard(), + logger: slog.New(slog.DiscardHandler), // retryablehttp defaults retryMax: 4, retryWaitMax: 30 * time.Second, - userAgent: UserAgentInfo{ - Version: "NA", - CommitSHA: "NA", - ScaleSetID: 0, - }, } + ac.userAgent.Store( + UserAgentInfo{ + Version: "N/A", + CommitSHA: "N/A", + ScaleSetID: 0, + }.String()) + for _, option := range options { option(ac) } retryClient := retryablehttp.NewClient() + retryClient.Logger = ac.logger retryClient.RetryMax = ac.retryMax retryClient.RetryWaitMax = ac.retryWaitMax @@ -194,46 +218,18 @@ func NewClient(githubConfigURL string, creds *ActionsAuth, options ...ClientOpti transport.Proxy = ac.proxyFunc retryClient.HTTPClient.Transport = transport - ac.Client = retryClient.StandardClient() + ac.httpClient = retryClient.StandardClient() return ac, nil } +// SetUserAgent updates the user agent func (c *Client) SetUserAgent(info UserAgentInfo) { - c.userAgent = info + c.userAgent.Store(info.String()) } -// Identifier returns a string to help identify a client uniquely. -// This is used for caching client instances and understanding when a config -// change warrants creating a new client. Any changes to Client that would -// require a new client should be reflected here. -func (c *Client) Identifier() string { - identifier := fmt.Sprintf("configURL:%q,", c.config.ConfigURL.String()) - - if c.creds.Token != "" { - identifier += fmt.Sprintf("token:%q,", c.creds.Token) - } - - if c.creds.AppCreds != nil { - identifier += fmt.Sprintf( - "appID:%q,installationID:%q,key:%q", - c.creds.AppCreds.AppID, - c.creds.AppCreds.AppInstallationID, - c.creds.AppCreds.AppPrivateKey, - ) - } - - if c.rootCAs != nil { - // ignoring because this cert pool is intended not to come from SystemCertPool - // nolint:staticcheck - identifier += fmt.Sprintf("rootCAs:%q", c.rootCAs.Subjects()) - } - - return uuid.NewHash(sha256.New(), uuid.NameSpaceOID, []byte(identifier), 6).String() -} - -func (c *Client) Do(req *http.Request) (*http.Response, error) { - resp, err := c.Client.Do(req) +func (c *Client) do(req *http.Request) (*http.Response, error) { + resp, err := c.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("client request failed: %w", err) } @@ -252,19 +248,19 @@ func (c *Client) Do(req *http.Request) (*http.Response, error) { return resp, nil } -func (c *Client) NewGitHubAPIRequest(ctx context.Context, method, path string, body io.Reader) (*http.Request, error) { +func (c *Client) newGitHubAPIRequest(ctx context.Context, method, path string, body io.Reader) (*http.Request, error) { 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) } - req.Header.Set("User-Agent", c.userAgent.String()) + req.Header.Set("User-Agent", c.userAgent.Load()) return req, nil } -func (c *Client) NewActionsServiceRequest(ctx context.Context, method, path string, body io.Reader) (*http.Request, error) { +func (c *Client) newActionsServiceRequest(ctx context.Context, method, path string, body io.Reader) (*http.Request, error) { err := c.updateTokenIfNeeded(ctx) if err != nil { return nil, fmt.Errorf("failed to issue update token if needed: %w", err) @@ -275,9 +271,9 @@ func (c *Client) NewActionsServiceRequest(ctx context.Context, method, path stri return nil, fmt.Errorf("failed to parse path %q: %w", path, err) } - urlString, err := url.JoinPath(c.ActionsServiceURL, parsedPath.Path) + urlString, err := url.JoinPath(c.actionsServiceURL, parsedPath.Path) if err != nil { - return nil, fmt.Errorf("failed to join path (actions_service_url=%q, parsedPath=%q): %w", c.ActionsServiceURL, parsedPath.Path, err) + return nil, fmt.Errorf("failed to join path (actions_service_url=%q, parsedPath=%q): %w", c.actionsServiceURL, parsedPath.Path, err) } u, err := url.Parse(urlString) @@ -299,43 +295,45 @@ func (c *Client) NewActionsServiceRequest(ctx context.Context, method, path stri } req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.ActionsServiceAdminToken)) - req.Header.Set("User-Agent", c.userAgent.String()) + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.actionsServiceAdminToken)) + req.Header.Set("User-Agent", c.userAgent.Load()) return req, nil } -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) +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) if err != nil { return nil, fmt.Errorf("failed to create new actions service request: %w", err) } - resp, err := c.Do(req) + resp, err := c.do(req) if err != nil { return nil, fmt.Errorf("failed to issue the request: %w", err) } + defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return nil, ParseActionsErrorFromResponse(resp) + return nil, parseActionsErrorFromResponse(resp) } var runnerScaleSetList *runnerScaleSetsResponse if err := json.NewDecoder(resp.Body).Decode(&runnerScaleSetList); err != nil { return nil, &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: err, } } if runnerScaleSetList.Count == 0 { return nil, nil } + if runnerScaleSetList.Count > 1 { return nil, &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: fmt.Errorf("multiple runner scale sets found with name %q", runnerScaleSetName), } } @@ -343,27 +341,28 @@ func (c *Client) GetRunnerScaleSet(ctx context.Context, runnerGroupId int, runne return &runnerScaleSetList.RunnerScaleSets[0], nil } -func (c *Client) GetRunnerScaleSetById(ctx context.Context, runnerScaleSetId int) (*RunnerScaleSet, error) { - path := fmt.Sprintf("/%s/%d", scaleSetEndpoint, runnerScaleSetId) - req, err := c.NewActionsServiceRequest(ctx, http.MethodGet, path, nil) +func (c *Client) GetRunnerScaleSetByID(ctx context.Context, runnerScaleSetID int) (*RunnerScaleSet, error) { + path := fmt.Sprintf("/%s/%d", scaleSetEndpoint, runnerScaleSetID) + req, err := c.newActionsServiceRequest(ctx, http.MethodGet, path, nil) if err != nil { return nil, fmt.Errorf("failed to create new actions service request: %w", err) } - resp, err := c.Do(req) + resp, err := c.do(req) if err != nil { return nil, fmt.Errorf("failed to issue the request: %w", err) } + defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return nil, ParseActionsErrorFromResponse(resp) + return nil, parseActionsErrorFromResponse(resp) } var runnerScaleSet *RunnerScaleSet if err := json.NewDecoder(resp.Body).Decode(&runnerScaleSet); err != nil { return nil, &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: err, } } @@ -372,12 +371,12 @@ func (c *Client) GetRunnerScaleSetById(ctx context.Context, runnerScaleSetId int 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) + req, err := c.newActionsServiceRequest(ctx, http.MethodGet, path, nil) if err != nil { return nil, fmt.Errorf("failed to create new actions service request: %w", err) } - resp, err := c.Do(req) + resp, err := c.do(req) if err != nil { return nil, fmt.Errorf("failed to issue the request: %w", err) } @@ -387,13 +386,13 @@ func (c *Client) GetRunnerGroupByName(ctx context.Context, runnerGroup string) ( if err != nil { return nil, &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: err, } } return nil, fmt.Errorf("unexpected status code: %w", &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: errors.New(string(body)), }) } @@ -403,7 +402,7 @@ func (c *Client) GetRunnerGroupByName(ctx context.Context, runnerGroup string) ( if err != nil { return nil, &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: err, } } @@ -411,7 +410,7 @@ func (c *Client) GetRunnerGroupByName(ctx context.Context, runnerGroup string) ( if runnerGroupList.Count == 0 { return nil, &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: fmt.Errorf("no runner group found with name %q", runnerGroup), } } @@ -419,7 +418,7 @@ func (c *Client) GetRunnerGroupByName(ctx context.Context, runnerGroup string) ( if runnerGroupList.Count > 1 { return nil, &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: fmt.Errorf("multiple runner group found with name %q", runnerGroup), } } @@ -433,92 +432,93 @@ func (c *Client) CreateRunnerScaleSet(ctx context.Context, runnerScaleSet *Runne return nil, fmt.Errorf("failed to marshal runner scale set: %w", err) } - req, err := c.NewActionsServiceRequest(ctx, http.MethodPost, scaleSetEndpoint, bytes.NewReader(body)) + req, err := c.newActionsServiceRequest(ctx, http.MethodPost, scaleSetEndpoint, bytes.NewReader(body)) if err != nil { return nil, fmt.Errorf("failed to create new actions service request: %w", err) } - resp, err := c.Do(req) + resp, err := c.do(req) if err != nil { return nil, fmt.Errorf("failed to issue the request: %w", err) } if resp.StatusCode != http.StatusOK { - return nil, ParseActionsErrorFromResponse(resp) + return nil, parseActionsErrorFromResponse(resp) } var createdRunnerScaleSet *RunnerScaleSet if err := json.NewDecoder(resp.Body).Decode(&createdRunnerScaleSet); err != nil { return nil, &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: err, } } return createdRunnerScaleSet, nil } -func (c *Client) UpdateRunnerScaleSet(ctx context.Context, runnerScaleSetId int, runnerScaleSet *RunnerScaleSet) (*RunnerScaleSet, error) { - path := fmt.Sprintf("%s/%d", scaleSetEndpoint, runnerScaleSetId) +func (c *Client) UpdateRunnerScaleSet(ctx context.Context, runnerScaleSetID int, runnerScaleSet *RunnerScaleSet) (*RunnerScaleSet, error) { + path := fmt.Sprintf("%s/%d", scaleSetEndpoint, runnerScaleSetID) body, err := json.Marshal(runnerScaleSet) if err != nil { return nil, fmt.Errorf("failed to marshal runner scale set: %w", err) } - req, err := c.NewActionsServiceRequest(ctx, http.MethodPatch, path, bytes.NewReader(body)) + req, err := c.newActionsServiceRequest(ctx, http.MethodPatch, path, bytes.NewReader(body)) if err != nil { return nil, fmt.Errorf("failed to create new actions service request: %w", err) } - resp, err := c.Do(req) + resp, err := c.do(req) if err != nil { return nil, fmt.Errorf("failed to issue the request: %w", err) } if resp.StatusCode != http.StatusOK { - return nil, ParseActionsErrorFromResponse(resp) + return nil, parseActionsErrorFromResponse(resp) } var updatedRunnerScaleSet *RunnerScaleSet if err := json.NewDecoder(resp.Body).Decode(&updatedRunnerScaleSet); err != nil { return nil, &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: err, } } return updatedRunnerScaleSet, nil } -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) +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) if err != nil { return fmt.Errorf("failed to create new actions service request: %w", err) } - resp, err := c.Do(req) + resp, err := c.do(req) if err != nil { return fmt.Errorf("failed to issue the request: %w", err) } + defer resp.Body.Close() if resp.StatusCode != http.StatusNoContent { - return ParseActionsErrorFromResponse(resp) + return parseActionsErrorFromResponse(resp) } - defer resp.Body.Close() return nil } -func (c *Client) GetMessage(ctx context.Context, messageQueueUrl, messageQueueAccessToken string, lastMessageId int64, maxCapacity int) (*RunnerScaleSetMessage, error) { - u, err := url.Parse(messageQueueUrl) +// GetMessage fetches a message from the runner scale set message queue. +func (c *Client) GetMessage(ctx context.Context, messageQueueURL, messageQueueAccessToken string, lastMessageID int64, maxCapacity int) (*RunnerScaleSetMessage, error) { + u, err := url.Parse(messageQueueURL) if err != nil { return nil, fmt.Errorf("failed to parse message queue url: %w", err) } - if lastMessageId > 0 { + if lastMessageID > 0 { q := u.Query() - q.Set("lastMessageId", strconv.FormatInt(lastMessageId, 10)) + q.Set("lastMessageId", strconv.FormatInt(lastMessageID, 10)) u.RawQuery = q.Encode() } @@ -533,36 +533,35 @@ func (c *Client) GetMessage(ctx context.Context, messageQueueUrl, messageQueueAc req.Header.Set("Accept", "application/json; api-version=6.0-preview") req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", messageQueueAccessToken)) - req.Header.Set("User-Agent", c.userAgent.String()) + req.Header.Set("User-Agent", c.userAgent.Load()) req.Header.Set(HeaderScaleSetMaxCapacity, strconv.Itoa(maxCapacity)) - resp, err := c.Do(req) + resp, err := c.do(req) if err != nil { return nil, fmt.Errorf("failed to issue the request: %w", err) } + defer resp.Body.Close() if resp.StatusCode == http.StatusAccepted { - defer resp.Body.Close() return nil, nil } if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusUnauthorized { - return nil, ParseActionsErrorFromResponse(resp) + return nil, parseActionsErrorFromResponse(resp) } - defer resp.Body.Close() body, err := io.ReadAll(resp.Body) body = trimByteOrderMark(body) if err != nil { return nil, &ActionsError{ - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), StatusCode: resp.StatusCode, Err: err, } } return nil, &MessageQueueTokenExpiredError{ - activityID: resp.Header.Get(HeaderActionsActivityID), + activityID: resp.Header.Get(headerActionsActivityID), statusCode: resp.StatusCode, msg: string(body), } @@ -572,20 +571,20 @@ func (c *Client) GetMessage(ctx context.Context, messageQueueUrl, messageQueueAc if err := json.NewDecoder(resp.Body).Decode(&message); err != nil { return nil, &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: err, } } return message, nil } -func (c *Client) DeleteMessage(ctx context.Context, messageQueueUrl, messageQueueAccessToken string, messageId int64) error { - u, err := url.Parse(messageQueueUrl) +func (c *Client) DeleteMessage(ctx context.Context, messageQueueURL, messageQueueAccessToken string, messageID int64) error { + u, err := url.Parse(messageQueueURL) if err != nil { return fmt.Errorf("failed to parse message queue url: %w", err) } - u.Path = fmt.Sprintf("%s/%d", u.Path, messageId) + u.Path = fmt.Sprintf("%s/%d", u.Path, messageID) req, err := http.NewRequestWithContext(ctx, http.MethodDelete, u.String(), nil) if err != nil { @@ -594,39 +593,40 @@ func (c *Client) DeleteMessage(ctx context.Context, messageQueueUrl, messageQueu req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", messageQueueAccessToken)) - req.Header.Set("User-Agent", c.userAgent.String()) + req.Header.Set("User-Agent", c.userAgent.Load()) - resp, err := c.Do(req) + resp, err := c.do(req) if err != nil { return fmt.Errorf("failed to issue the request: %w", err) } + defer resp.Body.Close() - if resp.StatusCode != http.StatusNoContent { - if resp.StatusCode != http.StatusUnauthorized { - return ParseActionsErrorFromResponse(resp) - } + if resp.StatusCode == http.StatusNoContent { + return nil + } - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - body = trimByteOrderMark(body) - if err != nil { - return &ActionsError{ - ActivityID: resp.Header.Get(HeaderActionsActivityID), - StatusCode: resp.StatusCode, - Err: err, - } - } - return &MessageQueueTokenExpiredError{ - activityID: resp.Header.Get(HeaderActionsActivityID), - statusCode: resp.StatusCode, - msg: string(body), + if resp.StatusCode != http.StatusUnauthorized { + return parseActionsErrorFromResponse(resp) + } + + body, err := io.ReadAll(resp.Body) + body = trimByteOrderMark(body) + if err != nil { + return &ActionsError{ + ActivityID: resp.Header.Get(headerActionsActivityID), + StatusCode: resp.StatusCode, + Err: err, } } - return nil + return &MessageQueueTokenExpiredError{ + activityID: resp.Header.Get(headerActionsActivityID), + statusCode: resp.StatusCode, + msg: string(body), + } } -func (c *Client) CreateMessageSession(ctx context.Context, runnerScaleSetId int, owner string) (*RunnerScaleSetSession, error) { - path := fmt.Sprintf("/%s/%d/sessions", scaleSetEndpoint, runnerScaleSetId) +func (c *Client) CreateMessageSession(ctx context.Context, runnerScaleSetID int, owner string) (*RunnerScaleSetSession, error) { + path := fmt.Sprintf("/%s/%d/sessions", scaleSetEndpoint, runnerScaleSetID) newSession := &RunnerScaleSetSession{ OwnerName: owner, @@ -646,13 +646,13 @@ func (c *Client) CreateMessageSession(ctx context.Context, runnerScaleSetId int, return createdSession, nil } -func (c *Client) DeleteMessageSession(ctx context.Context, runnerScaleSetId int, sessionId *uuid.UUID) error { - path := fmt.Sprintf("/%s/%d/sessions/%s", scaleSetEndpoint, runnerScaleSetId, sessionId.String()) +func (c *Client) DeleteMessageSession(ctx context.Context, runnerScaleSetID int, 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) } -func (c *Client) RefreshMessageSession(ctx context.Context, runnerScaleSetId int, sessionId *uuid.UUID) (*RunnerScaleSetSession, error) { - path := fmt.Sprintf("/%s/%d/sessions/%s", scaleSetEndpoint, runnerScaleSetId, sessionId.String()) +func (c *Client) RefreshMessageSession(ctx context.Context, runnerScaleSetID int, sessionID uuid.UUID) (*RunnerScaleSetSession, error) { + path := fmt.Sprintf("/%s/%d/sessions/%s", scaleSetEndpoint, runnerScaleSetID, sessionID.String()) refreshedSession := &RunnerScaleSetSession{} if err := c.doSessionRequest(ctx, http.MethodPatch, path, nil, http.StatusOK, refreshedSession); err != nil { return nil, fmt.Errorf("failed to do the session request: %w", err) @@ -661,15 +661,16 @@ func (c *Client) RefreshMessageSession(ctx context.Context, runnerScaleSetId int } func (c *Client) doSessionRequest(ctx context.Context, method, path string, requestData io.Reader, expectedResponseStatusCode int, responseUnmarshalTarget any) error { - req, err := c.NewActionsServiceRequest(ctx, method, path, requestData) + req, err := c.newActionsServiceRequest(ctx, method, path, requestData) if err != nil { return fmt.Errorf("failed to create new actions service request: %w", err) } - resp, err := c.Do(req) + resp, err := c.do(req) if err != nil { return fmt.Errorf("failed to issue the request: %w", err) } + defer resp.Body.Close() if resp.StatusCode == expectedResponseStatusCode { if responseUnmarshalTarget == nil { @@ -679,7 +680,7 @@ func (c *Client) doSessionRequest(ctx context.Context, method, path string, requ if err := json.NewDecoder(resp.Body).Decode(responseUnmarshalTarget); err != nil { return &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: err, } } @@ -688,31 +689,30 @@ func (c *Client) doSessionRequest(ctx context.Context, method, path string, requ } if resp.StatusCode >= 400 && resp.StatusCode < 500 { - return ParseActionsErrorFromResponse(resp) + return parseActionsErrorFromResponse(resp) } - defer resp.Body.Close() body, err := io.ReadAll(resp.Body) body = trimByteOrderMark(body) if err != nil { return &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: err, } } return fmt.Errorf("unexpected status code: %w", &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: errors.New(string(body)), }) } -func (c *Client) AcquireJobs(ctx context.Context, runnerScaleSetId int, messageQueueAccessToken string, requestIds []int64) ([]int64, error) { - u := fmt.Sprintf("%s/%s/%d/acquirejobs?api-version=6.0-preview", c.ActionsServiceURL, scaleSetEndpoint, runnerScaleSetId) +func (c *Client) AcquireJobs(ctx context.Context, runnerScaleSetID int, messageQueueAccessToken string, requestIDs []int64) ([]int64, error) { + u := fmt.Sprintf("%s/%s/%d/acquirejobs?api-version=6.0-preview", c.actionsServiceURL, scaleSetEndpoint, runnerScaleSetID) - body, err := json.Marshal(requestIds) + body, err := json.Marshal(requestIDs) if err != nil { return nil, fmt.Errorf("failed to marshal request ids: %w", err) } @@ -724,77 +724,75 @@ func (c *Client) AcquireJobs(ctx context.Context, runnerScaleSetId int, messageQ req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", messageQueueAccessToken)) - req.Header.Set("User-Agent", c.userAgent.String()) + req.Header.Set("User-Agent", c.userAgent.Load()) - resp, err := c.Do(req) + resp, err := c.do(req) if err != nil { return nil, fmt.Errorf("failed to issue the request: %w", err) } + defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - if resp.StatusCode != http.StatusUnauthorized { - return nil, ParseActionsErrorFromResponse(resp) - } - - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - body = trimByteOrderMark(body) - if err != nil { + if resp.StatusCode == http.StatusOK { + var acquiredJobs *Int64List + if err := json.NewDecoder(resp.Body).Decode(&acquiredJobs); err != nil { return nil, &ActionsError{ - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), StatusCode: resp.StatusCode, Err: err, } } - return nil, &MessageQueueTokenExpiredError{ - activityID: resp.Header.Get(HeaderActionsActivityID), - statusCode: resp.StatusCode, - msg: string(body), - } + return acquiredJobs.Value, nil } - var acquiredJobs *Int64List - err = json.NewDecoder(resp.Body).Decode(&acquiredJobs) + if resp.StatusCode != http.StatusUnauthorized { + return nil, parseActionsErrorFromResponse(resp) + } + + body, err = io.ReadAll(resp.Body) + body = trimByteOrderMark(body) if err != nil { return nil, &ActionsError{ - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), StatusCode: resp.StatusCode, Err: err, } } - return acquiredJobs.Value, nil + return nil, &MessageQueueTokenExpiredError{ + activityID: resp.Header.Get(headerActionsActivityID), + statusCode: resp.StatusCode, + msg: string(body), + } } -func (c *Client) GetAcquirableJobs(ctx context.Context, runnerScaleSetId int) (*AcquirableJobList, error) { - path := fmt.Sprintf("/%s/%d/acquirablejobs", scaleSetEndpoint, runnerScaleSetId) +func (c *Client) GetAcquirableJobs(ctx context.Context, runnerScaleSetID int) (*AcquirableJobList, error) { + path := fmt.Sprintf("/%s/%d/acquirablejobs", scaleSetEndpoint, runnerScaleSetID) - req, err := c.NewActionsServiceRequest(ctx, http.MethodGet, path, nil) + req, err := c.newActionsServiceRequest(ctx, http.MethodGet, path, nil) if err != nil { return nil, fmt.Errorf("failed to create new actions service request: %w", err) } - resp, err := c.Do(req) + resp, err := c.do(req) if err != nil { return nil, fmt.Errorf("failed to issue the request: %w", err) } + defer resp.Body.Close() if resp.StatusCode == http.StatusNoContent { - defer resp.Body.Close() return &AcquirableJobList{Count: 0, Jobs: []AcquirableJob{}}, nil } if resp.StatusCode != http.StatusOK { - return nil, ParseActionsErrorFromResponse(resp) + return nil, parseActionsErrorFromResponse(resp) } var acquirableJobList *AcquirableJobList - err = json.NewDecoder(resp.Body).Decode(&acquirableJobList) - if err != nil { + if err := json.NewDecoder(resp.Body).Decode(&acquirableJobList); err != nil { return nil, &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: err, } } @@ -802,61 +800,63 @@ func (c *Client) GetAcquirableJobs(ctx context.Context, runnerScaleSetId int) (* return acquirableJobList, nil } -func (c *Client) GenerateJitRunnerConfig(ctx context.Context, jitRunnerSetting *RunnerScaleSetJitRunnerSetting, scaleSetId int) (*RunnerScaleSetJitRunnerConfig, error) { - path := fmt.Sprintf("/%s/%d/generatejitconfig", scaleSetEndpoint, scaleSetId) +func (c *Client) GenerateJitRunnerConfig(ctx context.Context, jitRunnerSetting *RunnerScaleSetJitRunnerSetting, scaleSetID int) (*RunnerScaleSetJitRunnerConfig, error) { + path := fmt.Sprintf("/%s/%d/generatejitconfig", scaleSetEndpoint, scaleSetID) body, err := json.Marshal(jitRunnerSetting) if err != nil { return nil, fmt.Errorf("failed to marshal runner settings: %w", err) } - req, err := c.NewActionsServiceRequest(ctx, http.MethodPost, path, bytes.NewBuffer(body)) + req, err := c.newActionsServiceRequest(ctx, http.MethodPost, path, bytes.NewBuffer(body)) if err != nil { return nil, fmt.Errorf("failed to create new actions service request: %w", err) } - resp, err := c.Do(req) + resp, err := c.do(req) if err != nil { return nil, fmt.Errorf("failed to issue the request: %w", err) } + defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return nil, ParseActionsErrorFromResponse(resp) + return nil, parseActionsErrorFromResponse(resp) } var runnerJitConfig *RunnerScaleSetJitRunnerConfig if err := json.NewDecoder(resp.Body).Decode(&runnerJitConfig); err != nil { return nil, &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: err, } } return runnerJitConfig, nil } -func (c *Client) GetRunner(ctx context.Context, runnerId int64) (*RunnerReference, error) { - path := fmt.Sprintf("/%s/%d", runnerEndpoint, runnerId) +func (c *Client) GetRunner(ctx context.Context, runnerID int64) (*RunnerReference, error) { + path := fmt.Sprintf("/%s/%d", runnerEndpoint, runnerID) - req, err := c.NewActionsServiceRequest(ctx, http.MethodGet, path, nil) + req, err := c.newActionsServiceRequest(ctx, http.MethodGet, path, nil) if err != nil { return nil, fmt.Errorf("failed to create new actions service request: %w", err) } - resp, err := c.Do(req) + resp, err := c.do(req) if err != nil { return nil, fmt.Errorf("failed to issue the request: %w", err) } + defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return nil, ParseActionsErrorFromResponse(resp) + return nil, parseActionsErrorFromResponse(resp) } var runnerReference *RunnerReference if err := json.NewDecoder(resp.Body).Decode(&runnerReference); err != nil { return nil, &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: err, } } @@ -867,25 +867,26 @@ func (c *Client) GetRunner(ctx context.Context, runnerId int64) (*RunnerReferenc func (c *Client) GetRunnerByName(ctx context.Context, runnerName string) (*RunnerReference, error) { path := fmt.Sprintf("/%s?agentName=%s", runnerEndpoint, runnerName) - req, err := c.NewActionsServiceRequest(ctx, http.MethodGet, path, nil) + req, err := c.newActionsServiceRequest(ctx, http.MethodGet, path, nil) if err != nil { return nil, fmt.Errorf("failed to create new actions service request: %w", err) } - resp, err := c.Do(req) + resp, err := c.do(req) if err != nil { return nil, fmt.Errorf("failed to issue the request: %w", err) } + defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return nil, ParseActionsErrorFromResponse(resp) + return nil, parseActionsErrorFromResponse(resp) } var runnerList *RunnerReferenceList if err := json.NewDecoder(resp.Body).Decode(&runnerList); err != nil { return nil, &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: err, } } @@ -897,7 +898,7 @@ func (c *Client) GetRunnerByName(ctx context.Context, runnerName string) (*Runne if runnerList.Count > 1 { return nil, &ActionsError{ StatusCode: resp.StatusCode, - ActivityID: resp.Header.Get(HeaderActionsActivityID), + ActivityID: resp.Header.Get(headerActionsActivityID), Err: fmt.Errorf("multiple runner found with name %s", runnerName), } } @@ -905,24 +906,24 @@ func (c *Client) GetRunnerByName(ctx context.Context, runnerName string) (*Runne return &runnerList.RunnerReferences[0], nil } -func (c *Client) RemoveRunner(ctx context.Context, runnerId int64) error { - path := fmt.Sprintf("/%s/%d", runnerEndpoint, runnerId) +func (c *Client) RemoveRunner(ctx context.Context, runnerID int64) error { + path := fmt.Sprintf("/%s/%d", runnerEndpoint, runnerID) - req, err := c.NewActionsServiceRequest(ctx, http.MethodDelete, path, nil) + req, err := c.newActionsServiceRequest(ctx, http.MethodDelete, path, nil) if err != nil { return fmt.Errorf("failed to create new actions service request: %w", err) } - resp, err := c.Do(req) + resp, err := c.do(req) if err != nil { return fmt.Errorf("failed to issue the request: %w", err) } + defer resp.Body.Close() if resp.StatusCode != http.StatusNoContent { - return ParseActionsErrorFromResponse(resp) + return parseActionsErrorFromResponse(resp) } - defer resp.Body.Close() return nil } @@ -938,7 +939,7 @@ func (c *Client) getRunnerRegistrationToken(ctx context.Context) (*registrationT } var buf bytes.Buffer - req, err := c.NewGitHubAPIRequest(ctx, http.MethodPost, path, &buf) + req, err := c.newGitHubAPIRequest(ctx, http.MethodPost, path, &buf) if err != nil { return nil, fmt.Errorf("failed to create new GitHub API request: %w", err) } @@ -948,7 +949,7 @@ func (c *Client) getRunnerRegistrationToken(ctx context.Context) (*registrationT if c.creds.Token != "" { bearerToken = fmt.Sprintf("Bearer %v", c.creds.Token) } else { - accessToken, err := c.fetchAccessToken(ctx, c.config.ConfigURL.String(), c.creds.AppCreds) + accessToken, err := c.fetchAccessToken(ctx, c.creds.App) if err != nil { return nil, fmt.Errorf("failed to fetch access token: %w", err) } @@ -961,7 +962,7 @@ func (c *Client) getRunnerRegistrationToken(ctx context.Context) (*registrationT c.logger.Info("getting runner registration token", "registrationTokenURL", req.URL.String()) - resp, err := c.Do(req) + resp, err := c.do(req) if err != nil { return nil, fmt.Errorf("failed to issue the request: %w", err) } @@ -974,7 +975,7 @@ func (c *Client) getRunnerRegistrationToken(ctx context.Context) (*registrationT } return nil, &GitHubAPIError{ StatusCode: resp.StatusCode, - RequestID: resp.Header.Get(HeaderGitHubRequestID), + RequestID: resp.Header.Get(headerGitHubRequestID), Err: errors.New(string(body)), } } @@ -983,7 +984,7 @@ func (c *Client) getRunnerRegistrationToken(ctx context.Context) (*registrationT if err := json.NewDecoder(resp.Body).Decode(®istrationToken); err != nil { return nil, &GitHubAPIError{ StatusCode: resp.StatusCode, - RequestID: resp.Header.Get(HeaderGitHubRequestID), + RequestID: resp.Header.Get(headerGitHubRequestID), Err: err, } } @@ -997,14 +998,14 @@ type accessToken struct { ExpiresAt time.Time `json:"expires_at"` } -func (c *Client) fetchAccessToken(ctx context.Context, gitHubConfigURL string, creds *GitHubAppAuth) (*accessToken, error) { +func (c *Client) fetchAccessToken(ctx context.Context, creds *GitHubAppAuth) (*accessToken, error) { accessTokenJWT, err := createJWTForGitHubApp(creds) if err != nil { return nil, fmt.Errorf("failed to create JWT for GitHub app: %w", err) } path := fmt.Sprintf("/app/installations/%v/access_tokens", creds.AppInstallationID) - req, err := c.NewGitHubAPIRequest(ctx, http.MethodPost, path, nil) + req, err := c.newGitHubAPIRequest(ctx, http.MethodPost, path, nil) if err != nil { return nil, fmt.Errorf("failed to create new GitHub API request: %w", err) } @@ -1014,7 +1015,7 @@ func (c *Client) fetchAccessToken(ctx context.Context, gitHubConfigURL string, c c.logger.Info("getting access token for GitHub App auth", "accessTokenURL", req.URL.String()) - resp, err := c.Do(req) + resp, err := c.do(req) if err != nil { return nil, fmt.Errorf("failed to issue the request: %w", err) } @@ -1028,7 +1029,7 @@ func (c *Client) fetchAccessToken(ctx context.Context, gitHubConfigURL string, c return nil, &GitHubAPIError{ StatusCode: resp.StatusCode, - RequestID: resp.Header.Get(HeaderGitHubRequestID), + RequestID: resp.Header.Get(headerGitHubRequestID), Err: errors.New(errMsg), } } @@ -1038,7 +1039,7 @@ func (c *Client) fetchAccessToken(ctx context.Context, gitHubConfigURL string, c if err = json.NewDecoder(resp.Body).Decode(&accessToken); err != nil { return nil, &GitHubAPIError{ StatusCode: resp.StatusCode, - RequestID: resp.Header.Get(HeaderGitHubRequestID), + RequestID: resp.Header.Get(headerGitHubRequestID), Err: err, } } @@ -1046,7 +1047,7 @@ func (c *Client) fetchAccessToken(ctx context.Context, gitHubConfigURL string, c } type ActionsServiceAdminConnection struct { - ActionsServiceUrl *string `json:"url,omitempty"` + ActionsServiceURL *string `json:"url,omitempty"` AdminToken *string `json:"token,omitempty"` } @@ -1054,22 +1055,22 @@ func (c *Client) getActionsServiceAdminConnection(ctx context.Context, rt *regis path := "/actions/runner-registration" body := struct { - Url string `json:"url"` + URL string `json:"url"` RunnerEvent string `json:"runner_event"` }{ - Url: c.config.ConfigURL.String(), + URL: c.config.ConfigURL.String(), RunnerEvent: "register", } - buf := &bytes.Buffer{} - enc := json.NewEncoder(buf) + var buf bytes.Buffer + enc := json.NewEncoder(&buf) enc.SetEscapeHTML(false) if err := enc.Encode(body); err != nil { return nil, fmt.Errorf("failed to encode body: %w", err) } - req, err := c.NewGitHubAPIRequest(ctx, http.MethodPost, path, buf) + req, err := c.newGitHubAPIRequest(ctx, http.MethodPost, path, &buf) if err != nil { return nil, fmt.Errorf("failed to create new GitHub API request: %w", err) } @@ -1079,44 +1080,29 @@ func (c *Client) getActionsServiceAdminConnection(ctx context.Context, rt *regis c.logger.Info("getting Actions tenant URL and JWT", "registrationURL", req.URL.String()) - var resp *http.Response retry := 0 for { - var err error - resp, err = c.Do(req) - if err != nil { - return nil, fmt.Errorf("failed to issue the request: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode >= 200 && resp.StatusCode <= 299 { - break - } - - var innerErr error - body, err := io.ReadAll(resp.Body) - if err != nil { - innerErr = err - } else { - innerErr = errors.New(string(body)) - } - - if resp.StatusCode != http.StatusUnauthorized && resp.StatusCode != http.StatusForbidden { - return nil, &GitHubAPIError{ - StatusCode: resp.StatusCode, - RequestID: resp.Header.Get(HeaderGitHubRequestID), - Err: innerErr, - } + adminConnection, err := c.getActionsServiceAdminConnectionRequest(req) + if err == nil { + return adminConnection, nil } retry++ if retry > 5 { - return nil, fmt.Errorf("unable to register runner after 3 retries: %w", &GitHubAPIError{ - StatusCode: resp.StatusCode, - RequestID: resp.Header.Get(HeaderGitHubRequestID), - Err: innerErr, - }) + return nil, fmt.Errorf("unable to register runner after 5 retries: %w", err) } + + var ghErr *GitHubAPIError + if !errors.As(err, &ghErr) { + return nil, fmt.Errorf("failed to get actions service admin connection: %w", err) + } + + if ghErr.StatusCode != http.StatusUnauthorized && ghErr.StatusCode != http.StatusForbidden { + return nil, fmt.Errorf("failed to get actions service admin connection: %w", ghErr) + } + + c.logger.Debug("received unauthorized or forbidden response, retrying", "retryAttempt", retry, "statusCode", ghErr.StatusCode) + // Add exponential backoff + jitter to avoid thundering herd // This will generate a backoff schedule: // 1: 1s @@ -1127,25 +1113,50 @@ func (c *Client) getActionsServiceAdminConnection(ctx context.Context, rt *regis baseDelay := 500 * time.Millisecond jitter := time.Duration(rand.Intn(1000)) maxDelay := 20 * time.Second - delay := baseDelay*(1< maxDelay { - delay = maxDelay - } - - time.Sleep(delay) - } - - var actionsServiceAdminConnection *ActionsServiceAdminConnection - if err := json.NewDecoder(resp.Body).Decode(&actionsServiceAdminConnection); err != nil { - return nil, &GitHubAPIError{ - StatusCode: resp.StatusCode, - RequestID: resp.Header.Get(HeaderGitHubRequestID), - Err: err, + select { + case <-ctx.Done(): + return nil, fmt.Errorf("context cancelled while waiting to retry: %w", ctx.Err()) + case <-time.After(delay): + // continue to next retry } } +} - return actionsServiceAdminConnection, nil +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) + } + defer resp.Body.Close() + + if resp.StatusCode >= 200 && resp.StatusCode <= 299 { + var actionsServiceAdminConnection *ActionsServiceAdminConnection + if err := json.NewDecoder(resp.Body).Decode(&actionsServiceAdminConnection); err != nil { + return nil, &GitHubAPIError{ + StatusCode: resp.StatusCode, + RequestID: resp.Header.Get(headerGitHubRequestID), + Err: err, + } + } + + return actionsServiceAdminConnection, nil + } + + var innerErr error + body, err := io.ReadAll(resp.Body) + if err != nil { + innerErr = err + } else { + innerErr = errors.New(string(body)) + } + + return nil, &GitHubAPIError{ + StatusCode: resp.StatusCode, + RequestID: resp.Header.Get(headerGitHubRequestID), + Err: innerErr, + } } func createRegistrationTokenPath(config *GitHubConfig) (string, error) { @@ -1214,11 +1225,11 @@ func actionsServiceAdminTokenExpiresAt(jwtToken string) (time.Time, error) { } func (c *Client) updateTokenIfNeeded(ctx context.Context) error { - c.mu.Lock() - defer c.mu.Unlock() + c.actionsMu.Lock() + defer c.actionsMu.Unlock() - aboutToExpire := time.Now().Add(60 * time.Second).After(c.ActionsServiceAdminTokenExpiresAt) - if !aboutToExpire && !c.ActionsServiceAdminTokenExpiresAt.IsZero() { + aboutToExpire := time.Now().Add(60 * time.Second).After(c.actionsServiceAdminTokenExpiresAt) + if !aboutToExpire && !c.actionsServiceAdminTokenExpiresAt.IsZero() { return nil } @@ -1233,9 +1244,9 @@ func (c *Client) updateTokenIfNeeded(ctx context.Context) error { return fmt.Errorf("failed to get actions service admin connection on refresh: %w", err) } - c.ActionsServiceURL = *adminConnInfo.ActionsServiceUrl - c.ActionsServiceAdminToken = *adminConnInfo.AdminToken - c.ActionsServiceAdminTokenExpiresAt, err = actionsServiceAdminTokenExpiresAt(*adminConnInfo.AdminToken) + c.actionsServiceURL = *adminConnInfo.ActionsServiceURL + c.actionsServiceAdminToken = *adminConnInfo.AdminToken + c.actionsServiceAdminTokenExpiresAt, err = actionsServiceAdminTokenExpiresAt(*adminConnInfo.AdminToken) if err != nil { return fmt.Errorf("failed to get admin token expire at on refresh: %w", err) } diff --git a/client_generate_jit_test.go b/client_generate_jit_test.go index 05f59ed..9f32225 100644 --- a/client_generate_jit_test.go +++ b/client_generate_jit_test.go @@ -1,4 +1,4 @@ -package scaleset_test +package scaleset import ( "context" @@ -6,27 +6,26 @@ import ( "testing" "time" - "github.com/actions/scaleset" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestGenerateJitRunnerConfig(t *testing.T) { ctx := context.Background() - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } t.Run("Get JIT Config for Runner", func(t *testing.T) { - want := &scaleset.RunnerScaleSetJitRunnerConfig{} + want := &RunnerScaleSetJitRunnerConfig{} response := []byte(`{"count":1,"value":[{"id":1,"name":"scale-set-name"}]}`) - runnerSettings := &scaleset.RunnerScaleSetJitRunnerSetting{} + runnerSettings := &RunnerScaleSetJitRunnerSetting{} server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Write(response) })) - client, err := scaleset.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) @@ -35,7 +34,7 @@ func TestGenerateJitRunnerConfig(t *testing.T) { }) t.Run("Default retries on server error", func(t *testing.T) { - runnerSettings := &scaleset.RunnerScaleSetJitRunnerSetting{} + runnerSettings := &RunnerScaleSetJitRunnerSetting{} retryMax := 1 actualRetry := 0 @@ -46,11 +45,11 @@ func TestGenerateJitRunnerConfig(t *testing.T) { actualRetry++ })) - client, err := scaleset.NewClient( + client, err := NewClient( server.configURLForOrg("my-org"), auth, - scaleset.WithRetryMax(1), - scaleset.WithRetryWaitMax(1*time.Millisecond), + WithRetryMax(1), + WithRetryWaitMax(1*time.Millisecond), ) require.NoError(t, err) diff --git a/client_job_acquisition_test.go b/client_job_acquisition_test.go index 4e761e9..5ec767b 100644 --- a/client_job_acquisition_test.go +++ b/client_job_acquisition_test.go @@ -1,4 +1,4 @@ -package scaleset_test +package scaleset import ( "context" @@ -8,14 +8,13 @@ import ( "testing" "time" - "github.com/actions/scaleset" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestAcquireJobs(t *testing.T) { ctx := context.Background() - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } @@ -23,8 +22,8 @@ func TestAcquireJobs(t *testing.T) { want := []int64{1} response := []byte(`{"value": [1]}`) - session := &scaleset.RunnerScaleSetSession{ - RunnerScaleSet: &scaleset.RunnerScaleSet{Id: 1}, + session := &RunnerScaleSetSession{ + RunnerScaleSet: &RunnerScaleSet{ID: 1}, MessageQueueAccessToken: "abc", } requestIDs := want @@ -38,23 +37,23 @@ func TestAcquireJobs(t *testing.T) { w.Write(response) })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.GetAcquirableJobs(ctx, 1) require.NoError(t, err) - got, err := client.AcquireJobs(ctx, session.RunnerScaleSet.Id, session.MessageQueueAccessToken, requestIDs) + got, err := client.AcquireJobs(ctx, session.RunnerScaleSet.ID, session.MessageQueueAccessToken, requestIDs) require.NoError(t, err) assert.Equal(t, want, got) }) t.Run("Default retries on server error", func(t *testing.T) { - session := &scaleset.RunnerScaleSetSession{ - RunnerScaleSet: &scaleset.RunnerScaleSet{Id: 1}, + session := &RunnerScaleSetSession{ + RunnerScaleSet: &RunnerScaleSet{ID: 1}, MessageQueueAccessToken: "abc", } - var requestIDs = []int64{1} + requestIDs := []int64{1} retryMax := 1 actualRetry := 0 @@ -70,18 +69,18 @@ func TestAcquireJobs(t *testing.T) { actualRetry++ })) - client, err := scaleset.NewClient( + client, err := NewClient( server.configURLForOrg("my-org"), auth, - scaleset.WithRetryMax(retryMax), - scaleset.WithRetryWaitMax(1*time.Millisecond), + WithRetryMax(retryMax), + WithRetryWaitMax(1*time.Millisecond), ) require.NoError(t, err) _, err = client.GetAcquirableJobs(ctx, 1) require.NoError(t, err) - _, err = client.AcquireJobs(context.Background(), session.RunnerScaleSet.Id, session.MessageQueueAccessToken, requestIDs) + _, err = client.AcquireJobs(context.Background(), session.RunnerScaleSet.ID, session.MessageQueueAccessToken, requestIDs) assert.NotNil(t, err) assert.Equalf(t, actualRetry, expectedRetry, "A retry was expected after the first request but got: %v", actualRetry) }) @@ -89,8 +88,8 @@ func TestAcquireJobs(t *testing.T) { t.Run("Should return MessageQueueTokenExpiredError when http error is not Unauthorized", func(t *testing.T) { want := []int64{1} - session := &scaleset.RunnerScaleSetSession{ - RunnerScaleSet: &scaleset.RunnerScaleSet{Id: 1}, + session := &RunnerScaleSetSession{ + RunnerScaleSet: &RunnerScaleSet{ID: 1}, MessageQueueAccessToken: "abc", } requestIDs := want @@ -106,45 +105,45 @@ func TestAcquireJobs(t *testing.T) { } })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.GetAcquirableJobs(ctx, 1) require.NoError(t, err) - got, err := client.AcquireJobs(ctx, session.RunnerScaleSet.Id, session.MessageQueueAccessToken, requestIDs) + got, err := client.AcquireJobs(ctx, session.RunnerScaleSet.ID, session.MessageQueueAccessToken, requestIDs) require.Error(t, err) assert.Nil(t, got) - var expectedErr *scaleset.MessageQueueTokenExpiredError + var expectedErr *MessageQueueTokenExpiredError assert.True(t, errors.As(err, &expectedErr)) }) } func TestGetAcquirableJobs(t *testing.T) { - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } t.Run("Acquire Job", func(t *testing.T) { - want := &scaleset.AcquirableJobList{} + want := &AcquirableJobList{} response := []byte(`{"count": 0}`) - runnerScaleSet := &scaleset.RunnerScaleSet{Id: 1} + runnerScaleSet := &RunnerScaleSet{ID: 1} server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Write(response) })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) - got, err := client.GetAcquirableJobs(context.Background(), runnerScaleSet.Id) + got, err := client.GetAcquirableJobs(context.Background(), runnerScaleSet.ID) require.NoError(t, err) assert.Equal(t, want, got) }) t.Run("Default retries on server error", func(t *testing.T) { - runnerScaleSet := &scaleset.RunnerScaleSet{Id: 1} + runnerScaleSet := &RunnerScaleSet{ID: 1} retryMax := 1 @@ -156,15 +155,15 @@ func TestGetAcquirableJobs(t *testing.T) { actualRetry++ })) - client, err := scaleset.NewClient( + client, err := NewClient( server.configURLForOrg("my-org"), auth, - scaleset.WithRetryMax(retryMax), - scaleset.WithRetryWaitMax(1*time.Millisecond), + WithRetryMax(retryMax), + WithRetryWaitMax(1*time.Millisecond), ) require.NoError(t, err) - _, err = client.GetAcquirableJobs(context.Background(), runnerScaleSet.Id) + _, err = client.GetAcquirableJobs(context.Background(), runnerScaleSet.ID) require.Error(t, err) assert.Equalf(t, actualRetry, expectedRetry, "A retry was expected after the first request but got: %v", actualRetry) }) diff --git a/client_proxy_test.go b/client_proxy_test.go index 8e81e1d..8cfe855 100644 --- a/client_proxy_test.go +++ b/client_proxy_test.go @@ -1,11 +1,10 @@ -package scaleset_test +package scaleset import ( "net/http" "net/url" "testing" - "github.com/actions/scaleset" "github.com/actions/scaleset/testserver" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -26,13 +25,13 @@ func TestClientProxy(t *testing.T) { return proxyConfig.ProxyFunc()(req.URL) } - c, err := scaleset.NewClient("http://github.com/org/repo", nil, scaleset.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) require.NoError(t, err) - _, err = c.Do(req) + _, err = c.do(req) require.NoError(t, err) assert.True(t, serverCalled) diff --git a/client_runner_scale_set_message_test.go b/client_runner_scale_set_message_test.go index 2ed3254..5618d6f 100644 --- a/client_runner_scale_set_message_test.go +++ b/client_runner_scale_set_message_test.go @@ -1,4 +1,4 @@ -package scaleset_test +package scaleset import ( "context" @@ -9,20 +9,19 @@ import ( "testing" "time" - "github.com/actions/scaleset" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestGetMessage(t *testing.T) { ctx := context.Background() - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjI1MTYyMzkwMjJ9.tlrHslTmDkoqnc4Kk9ISoKoUNDfHo-kjlH-ByISBqzE" - runnerScaleSetMessage := &scaleset.RunnerScaleSetMessage{ - MessageId: 1, + runnerScaleSetMessage := &RunnerScaleSetMessage{ + MessageID: 1, MessageType: "rssType", } @@ -33,7 +32,7 @@ func TestGetMessage(t *testing.T) { w.Write(response) })) - client, err := scaleset.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) @@ -50,7 +49,7 @@ func TestGetMessage(t *testing.T) { w.Write(response) })) - client, err := scaleset.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) @@ -69,11 +68,11 @@ func TestGetMessage(t *testing.T) { actualRetry++ })) - client, err := scaleset.NewClient( + client, err := NewClient( server.configURLForOrg("my-org"), auth, - scaleset.WithRetryMax(retryMax), - scaleset.WithRetryWaitMax(1*time.Millisecond), + WithRetryMax(retryMax), + WithRetryWaitMax(1*time.Millisecond), ) require.NoError(t, err) @@ -87,18 +86,18 @@ func TestGetMessage(t *testing.T) { w.WriteHeader(http.StatusUnauthorized) })) - client, err := scaleset.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) require.NotNil(t, err) - var expectedErr *scaleset.MessageQueueTokenExpiredError + var expectedErr *MessageQueueTokenExpiredError require.True(t, errors.As(err, &expectedErr)) }) t.Run("Status code not found", func(t *testing.T) { - want := scaleset.ActionsError{ + want := ActionsError{ Err: errors.New("unknown exception"), StatusCode: 404, } @@ -106,7 +105,7 @@ func TestGetMessage(t *testing.T) { w.WriteHeader(http.StatusNotFound) })) - client, err := scaleset.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) @@ -120,7 +119,7 @@ func TestGetMessage(t *testing.T) { w.Header().Set("Content-Type", "text/plain") })) - client, err := scaleset.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) @@ -129,7 +128,7 @@ func TestGetMessage(t *testing.T) { t.Run("Capacity error handling", func(t *testing.T) { server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - hc := r.Header.Get(scaleset.HeaderScaleSetMaxCapacity) + hc := r.Header.Get(HeaderScaleSetMaxCapacity) c, err := strconv.Atoi(hc) require.NoError(t, err) assert.GreaterOrEqual(t, c, 0) @@ -138,17 +137,17 @@ func TestGetMessage(t *testing.T) { w.Header().Set("Content-Type", "text/plain") })) - client, err := scaleset.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, -1) require.Error(t, err) // Ensure we don't send requests with negative capacity - assert.False(t, errors.Is(err, &scaleset.ActionsError{})) + assert.False(t, errors.Is(err, &ActionsError{})) _, err = client.GetMessage(ctx, server.URL, token, 0, 0) assert.Error(t, err) - var expectedErr *scaleset.ActionsError + var expectedErr *ActionsError assert.ErrorAs(t, err, &expectedErr) assert.Equal(t, http.StatusBadRequest, expectedErr.StatusCode) }) @@ -156,13 +155,13 @@ func TestGetMessage(t *testing.T) { func TestDeleteMessage(t *testing.T) { ctx := context.Background() - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjI1MTYyMzkwMjJ9.tlrHslTmDkoqnc4Kk9ISoKoUNDfHo-kjlH-ByISBqzE" - runnerScaleSetMessage := &scaleset.RunnerScaleSetMessage{ - MessageId: 1, + runnerScaleSetMessage := &RunnerScaleSetMessage{ + MessageID: 1, MessageType: "rssType", } @@ -171,10 +170,10 @@ func TestDeleteMessage(t *testing.T) { w.WriteHeader(http.StatusNoContent) })) - client, err := scaleset.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) + err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageID) assert.Nil(t, err) }) @@ -183,12 +182,12 @@ func TestDeleteMessage(t *testing.T) { w.WriteHeader(http.StatusUnauthorized) })) - client, err := scaleset.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) require.NotNil(t, err) - var expectedErr *scaleset.MessageQueueTokenExpiredError + var expectedErr *MessageQueueTokenExpiredError assert.True(t, errors.As(err, &expectedErr)) }) @@ -198,12 +197,12 @@ func TestDeleteMessage(t *testing.T) { w.Header().Set("Content-Type", "text/plain") })) - client, err := scaleset.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) + err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageID) require.NotNil(t, err) - var expectedErr *scaleset.ActionsError + var expectedErr *ActionsError assert.True(t, errors.As(err, &expectedErr)) }, ) @@ -216,21 +215,21 @@ func TestDeleteMessage(t *testing.T) { })) retryMax := 1 - client, err := scaleset.NewClient( + client, err := NewClient( server.configURLForOrg("my-org"), auth, - scaleset.WithRetryMax(retryMax), - scaleset.WithRetryWaitMax(1*time.Nanosecond), + WithRetryMax(retryMax), + WithRetryWaitMax(1*time.Nanosecond), ) require.NoError(t, err) - err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageId) + err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageID) assert.NotNil(t, err) expectedRetry := retryMax + 1 assert.Equalf(t, actualRetry, expectedRetry, "A retry was expected after the first request but got: %v", actualRetry) }) t.Run("No message found", func(t *testing.T) { - want := (*scaleset.RunnerScaleSetMessage)(nil) + want := (*RunnerScaleSetMessage)(nil) rsl, err := json.Marshal(want) require.NoError(t, err) @@ -238,11 +237,11 @@ func TestDeleteMessage(t *testing.T) { w.Write(rsl) })) - client, err := scaleset.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) - var expectedErr *scaleset.ActionsError + err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageID+1) + var expectedErr *ActionsError require.True(t, errors.As(err, &expectedErr)) }) } diff --git a/client_runner_scale_set_session_test.go b/client_runner_scale_set_session_test.go index 19b2533..c69589e 100644 --- a/client_runner_scale_set_session_test.go +++ b/client_runner_scale_set_session_test.go @@ -1,4 +1,4 @@ -package scaleset_test +package scaleset import ( "context" @@ -7,7 +7,6 @@ import ( "testing" "time" - "github.com/actions/scaleset" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -17,26 +16,26 @@ const exampleRequestID = "5ddf2050-dae0-013c-9159-04421ad31b68" func TestCreateMessageSession(t *testing.T) { ctx := context.Background() - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } t.Run("CreateMessageSession unmarshals correctly", func(t *testing.T) { owner := "foo" - runnerScaleSet := scaleset.RunnerScaleSet{ - Id: 1, + runnerScaleSet := RunnerScaleSet{ + ID: 1, Name: "ScaleSet", CreatedOn: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), - RunnerSetting: scaleset.RunnerSetting{}, + RunnerSetting: RunnerSetting{}, } - want := &scaleset.RunnerScaleSetSession{ + want := &RunnerScaleSetSession{ OwnerName: "foo", - RunnerScaleSet: &scaleset.RunnerScaleSet{ - Id: 1, + RunnerScaleSet: &RunnerScaleSet{ + ID: 1, Name: "ScaleSet", }, - MessageQueueUrl: "http://fake.scaleset.github.com/123", + MessageQueueURL: "http://fake.github.com/123", MessageQueueAccessToken: "fake.jwt.here", } @@ -47,33 +46,33 @@ func TestCreateMessageSession(t *testing.T) { "id": 1, "name": "ScaleSet" }, - "messageQueueUrl": "http://fake.scaleset.github.com/123", + "messageQueueUrl": "http://fake.github.com/123", "messageQueueAccessToken": "fake.jwt.here" }`) w.Write(resp) })) - client, err := scaleset.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) + got, err := client.CreateMessageSession(ctx, runnerScaleSet.ID, owner) require.NoError(t, err) assert.Equal(t, want, got) }) t.Run("CreateMessageSession unmarshals errors into ActionsError", func(t *testing.T) { owner := "foo" - runnerScaleSet := scaleset.RunnerScaleSet{ - Id: 1, + runnerScaleSet := RunnerScaleSet{ + ID: 1, Name: "ScaleSet", CreatedOn: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), - RunnerSetting: scaleset.RunnerSetting{}, + RunnerSetting: RunnerSetting{}, } - want := &scaleset.ActionsError{ + want := &ActionsError{ ActivityID: exampleRequestID, StatusCode: http.StatusBadRequest, - Err: &scaleset.ActionsExceptionError{ + Err: &ActionsExceptionError{ ExceptionName: "CSharpExceptionNameHere", Message: "could not do something", }, @@ -81,19 +80,19 @@ func TestCreateMessageSession(t *testing.T) { server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") - w.Header().Set(scaleset.HeaderActionsActivityID, exampleRequestID) + w.Header().Set(headerActionsActivityID, exampleRequestID) w.WriteHeader(http.StatusBadRequest) resp := []byte(`{"typeName": "CSharpExceptionNameHere","message": "could not do something"}`) w.Write(resp) })) - client, err := scaleset.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) + _, err = client.CreateMessageSession(ctx, runnerScaleSet.ID, owner) require.NotNil(t, err) - errorTypeForComparison := &scaleset.ActionsError{} + errorTypeForComparison := &ActionsError{} assert.True( t, errors.As(err, &errorTypeForComparison), @@ -106,11 +105,11 @@ func TestCreateMessageSession(t *testing.T) { t.Run("CreateMessageSession call is retried the correct amount of times", func(t *testing.T) { owner := "foo" - runnerScaleSet := scaleset.RunnerScaleSet{ - Id: 1, + runnerScaleSet := RunnerScaleSet{ + ID: 1, Name: "ScaleSet", CreatedOn: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), - RunnerSetting: scaleset.RunnerSetting{}, + RunnerSetting: RunnerSetting{}, } gotRetries := 0 @@ -124,15 +123,15 @@ func TestCreateMessageSession(t *testing.T) { wantRetries := retryMax + 1 - client, err := scaleset.NewClient( + client, err := NewClient( server.configURLForOrg("my-org"), auth, - scaleset.WithRetryMax(retryMax), - scaleset.WithRetryWaitMax(retryWaitMax), + WithRetryMax(retryMax), + WithRetryWaitMax(retryWaitMax), ) require.NoError(t, err) - _, err = client.CreateMessageSession(ctx, runnerScaleSet.Id, owner) + _, err = client.CreateMessageSession(ctx, runnerScaleSet.ID, owner) assert.NotNil(t, err) assert.Equalf(t, gotRetries, wantRetries, "CreateMessageSession got unexpected retry count: got=%v, want=%v", gotRetries, wantRetries) }) @@ -140,16 +139,16 @@ func TestCreateMessageSession(t *testing.T) { func TestDeleteMessageSession(t *testing.T) { ctx := context.Background() - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } t.Run("DeleteMessageSession call is retried the correct amount of times", func(t *testing.T) { - runnerScaleSet := scaleset.RunnerScaleSet{ - Id: 1, + runnerScaleSet := RunnerScaleSet{ + ID: 1, Name: "ScaleSet", CreatedOn: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), - RunnerSetting: scaleset.RunnerSetting{}, + RunnerSetting: RunnerSetting{}, } gotRetries := 0 @@ -163,33 +162,33 @@ func TestDeleteMessageSession(t *testing.T) { wantRetries := retryMax + 1 - client, err := scaleset.NewClient( + client, err := NewClient( server.configURLForOrg("my-org"), auth, - scaleset.WithRetryMax(retryMax), - scaleset.WithRetryWaitMax(retryWaitMax), + WithRetryMax(retryMax), + WithRetryWaitMax(retryWaitMax), ) require.NoError(t, err) - sessionId := uuid.New() + sessionID := uuid.New() - err = client.DeleteMessageSession(ctx, runnerScaleSet.Id, &sessionId) + err = client.DeleteMessageSession(ctx, runnerScaleSet.ID, sessionID) assert.NotNil(t, err) assert.Equalf(t, gotRetries, wantRetries, "CreateMessageSession got unexpected retry count: got=%v, want=%v", gotRetries, wantRetries) }) } func TestRefreshMessageSession(t *testing.T) { - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } t.Run("RefreshMessageSession call is retried the correct amount of times", func(t *testing.T) { - runnerScaleSet := scaleset.RunnerScaleSet{ - Id: 1, + runnerScaleSet := RunnerScaleSet{ + ID: 1, Name: "ScaleSet", CreatedOn: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), - RunnerSetting: scaleset.RunnerSetting{}, + RunnerSetting: RunnerSetting{}, } gotRetries := 0 @@ -203,17 +202,17 @@ func TestRefreshMessageSession(t *testing.T) { wantRetries := retryMax + 1 - client, err := scaleset.NewClient( + client, err := NewClient( server.configURLForOrg("my-org"), auth, - scaleset.WithRetryMax(retryMax), - scaleset.WithRetryWaitMax(retryWaitMax), + WithRetryMax(retryMax), + WithRetryWaitMax(retryWaitMax), ) require.NoError(t, err) - sessionId := uuid.New() + sessionID := uuid.New() - _, err = client.RefreshMessageSession(context.Background(), runnerScaleSet.Id, &sessionId) + _, err = client.RefreshMessageSession(context.Background(), runnerScaleSet.ID, sessionID) assert.NotNil(t, err) assert.Equalf(t, gotRetries, wantRetries, "CreateMessageSession got unexpected retry count: got=%v, want=%v", gotRetries, wantRetries) }) diff --git a/client_runner_scale_set_test.go b/client_runner_scale_set_test.go index 5cedcdf..82e2543 100644 --- a/client_runner_scale_set_test.go +++ b/client_runner_scale_set_test.go @@ -1,4 +1,4 @@ -package scaleset_test +package scaleset import ( "context" @@ -10,7 +10,6 @@ import ( "testing" "time" - "github.com/actions/scaleset" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -18,12 +17,12 @@ import ( func TestGetRunnerScaleSet(t *testing.T) { ctx := context.Background() - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } scaleSetName := "ScaleSet" - runnerScaleSet := scaleset.RunnerScaleSet{Id: 1, Name: scaleSetName} + runnerScaleSet := RunnerScaleSet{ID: 1, Name: scaleSetName} t.Run("Get existing scale set", func(t *testing.T) { want := &runnerScaleSet @@ -32,7 +31,7 @@ func TestGetRunnerScaleSet(t *testing.T) { w.Write(runnerScaleSetsResp) })) - client, err := scaleset.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) @@ -48,7 +47,7 @@ func TestGetRunnerScaleSet(t *testing.T) { url = *r.URL })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.GetRunnerScaleSet(ctx, 1, scaleSetName) @@ -65,7 +64,7 @@ func TestGetRunnerScaleSet(t *testing.T) { w.WriteHeader(http.StatusNotFound) })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.GetRunnerScaleSet(ctx, 1, scaleSetName) @@ -78,7 +77,7 @@ func TestGetRunnerScaleSet(t *testing.T) { w.Header().Set("Content-Type", "text/plain") })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.GetRunnerScaleSet(ctx, 1, scaleSetName) @@ -95,11 +94,11 @@ func TestGetRunnerScaleSet(t *testing.T) { retryMax := 1 retryWaitMax := 1 * time.Microsecond - client, err := scaleset.NewClient( + client, err := NewClient( server.configURLForOrg("my-org"), auth, - scaleset.WithRetryMax(retryMax), - scaleset.WithRetryWaitMax(retryWaitMax), + WithRetryMax(retryMax), + WithRetryWaitMax(retryWaitMax), ) require.NoError(t, err) @@ -110,13 +109,13 @@ func TestGetRunnerScaleSet(t *testing.T) { }) t.Run("RunnerScaleSet count is zero", func(t *testing.T) { - want := (*scaleset.RunnerScaleSet)(nil) + want := (*RunnerScaleSet)(nil) runnerScaleSetsResp := []byte(`{"count":0,"value":[{"id":1,"name":"ScaleSet"}]}`) server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Write(runnerScaleSetsResp) })) - client, err := scaleset.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) @@ -126,18 +125,18 @@ func TestGetRunnerScaleSet(t *testing.T) { t.Run("Multiple runner scale sets found", func(t *testing.T) { reqID := uuid.NewString() - wantErr := &scaleset.ActionsError{ + wantErr := &ActionsError{ StatusCode: http.StatusOK, ActivityID: reqID, Err: fmt.Errorf("multiple runner scale sets found with name %q", scaleSetName), } runnerScaleSetsResp := []byte(`{"count":2,"value":[{"id":1,"name":"ScaleSet"}]}`) server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set(scaleset.HeaderActionsActivityID, reqID) + w.Header().Set(headerActionsActivityID, reqID) w.Write(runnerScaleSetsResp) })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.GetRunnerScaleSet(ctx, 1, scaleSetName) @@ -146,14 +145,14 @@ func TestGetRunnerScaleSet(t *testing.T) { }) } -func TestGetRunnerScaleSetById(t *testing.T) { +func TestGetRunnerScaleSetByID(t *testing.T) { ctx := context.Background() - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } scaleSetCreationDateTime := time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC) - runnerScaleSet := scaleset.RunnerScaleSet{Id: 1, Name: "ScaleSet", CreatedOn: scaleSetCreationDateTime, RunnerSetting: scaleset.RunnerSetting{}} + runnerScaleSet := RunnerScaleSet{ID: 1, Name: "ScaleSet", CreatedOn: scaleSetCreationDateTime, RunnerSetting: RunnerSetting{}} t.Run("Get existing scale set by Id", func(t *testing.T) { want := &runnerScaleSet @@ -163,15 +162,15 @@ func TestGetRunnerScaleSetById(t *testing.T) { w.Write(rsl) })) - client, err := scaleset.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) + got, err := client.GetRunnerScaleSetByID(ctx, runnerScaleSet.ID) require.NoError(t, err) assert.Equal(t, want, got) }) - t.Run("GetRunnerScaleSetById calls correct url", func(t *testing.T) { + t.Run("GetRunnerScaleSetByID calls correct url", func(t *testing.T) { rsl, err := json.Marshal(&runnerScaleSet) require.NoError(t, err) @@ -181,13 +180,13 @@ func TestGetRunnerScaleSetById(t *testing.T) { url = *r.URL })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) - _, err = client.GetRunnerScaleSetById(ctx, runnerScaleSet.Id) + _, err = client.GetRunnerScaleSetByID(ctx, runnerScaleSet.ID) require.NoError(t, err) - expectedPath := fmt.Sprintf("/tenant/123/_apis/runtime/runnerscalesets/%d", runnerScaleSet.Id) + expectedPath := fmt.Sprintf("/tenant/123/_apis/runtime/runnerscalesets/%d", runnerScaleSet.ID) assert.Equal(t, expectedPath, url.Path) assert.Equal(t, "6.0-preview", url.Query().Get("api-version")) }) @@ -197,10 +196,10 @@ func TestGetRunnerScaleSetById(t *testing.T) { w.WriteHeader(http.StatusNotFound) })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) - _, err = client.GetRunnerScaleSetById(ctx, runnerScaleSet.Id) + _, err = client.GetRunnerScaleSetByID(ctx, runnerScaleSet.ID) assert.NotNil(t, err) }) @@ -210,10 +209,10 @@ func TestGetRunnerScaleSetById(t *testing.T) { w.Header().Set("Content-Type", "text/plain") })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) - _, err = client.GetRunnerScaleSetById(ctx, runnerScaleSet.Id) + _, err = client.GetRunnerScaleSetByID(ctx, runnerScaleSet.ID) assert.NotNil(t, err) }) @@ -226,32 +225,32 @@ func TestGetRunnerScaleSetById(t *testing.T) { retryMax := 1 retryWaitMax := 1 * time.Microsecond - client, err := scaleset.NewClient( + client, err := NewClient( server.configURLForOrg("my-org"), auth, - scaleset.WithRetryMax(retryMax), - scaleset.WithRetryWaitMax(retryWaitMax), + WithRetryMax(retryMax), + WithRetryWaitMax(retryWaitMax), ) require.NoError(t, err) - _, err = client.GetRunnerScaleSetById(ctx, runnerScaleSet.Id) + _, err = client.GetRunnerScaleSetByID(ctx, runnerScaleSet.ID) require.NotNil(t, err) expectedRetry := retryMax + 1 assert.Equalf(t, actualRetry, expectedRetry, "A retry was expected after the first request but got: %v", actualRetry) }) t.Run("No RunnerScaleSet found", func(t *testing.T) { - want := (*scaleset.RunnerScaleSet)(nil) + want := (*RunnerScaleSet)(nil) rsl, err := json.Marshal(want) require.NoError(t, err) server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Write(rsl) })) - client, err := scaleset.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) + got, err := client.GetRunnerScaleSetByID(ctx, runnerScaleSet.ID) require.NoError(t, err) assert.Equal(t, want, got) }) @@ -259,12 +258,12 @@ func TestGetRunnerScaleSetById(t *testing.T) { func TestCreateRunnerScaleSet(t *testing.T) { ctx := context.Background() - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } scaleSetCreationDateTime := time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC) - runnerScaleSet := scaleset.RunnerScaleSet{Id: 1, Name: "ScaleSet", CreatedOn: scaleSetCreationDateTime, RunnerSetting: scaleset.RunnerSetting{}} + runnerScaleSet := RunnerScaleSet{ID: 1, Name: "ScaleSet", CreatedOn: scaleSetCreationDateTime, RunnerSetting: RunnerSetting{}} t.Run("Create runner scale set", func(t *testing.T) { want := &runnerScaleSet @@ -274,7 +273,7 @@ func TestCreateRunnerScaleSet(t *testing.T) { w.Write(rsl) })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.CreateRunnerScaleSet(ctx, &runnerScaleSet) @@ -291,7 +290,7 @@ func TestCreateRunnerScaleSet(t *testing.T) { url = *r.URL })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.CreateRunnerScaleSet(ctx, &runnerScaleSet) @@ -308,12 +307,12 @@ func TestCreateRunnerScaleSet(t *testing.T) { w.Header().Set("Content-Type", "text/plain") })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.CreateRunnerScaleSet(ctx, &runnerScaleSet) require.NotNil(t, err) - var expectedErr *scaleset.ActionsError + var expectedErr *ActionsError assert.True(t, errors.As(err, &expectedErr)) }) @@ -327,11 +326,11 @@ func TestCreateRunnerScaleSet(t *testing.T) { retryMax := 1 retryWaitMax := 1 * time.Microsecond - client, err := scaleset.NewClient( + client, err := NewClient( server.configURLForOrg("my-org"), auth, - scaleset.WithRetryMax(retryMax), - scaleset.WithRetryWaitMax(retryWaitMax), + WithRetryMax(retryMax), + WithRetryWaitMax(retryWaitMax), ) require.NoError(t, err) @@ -344,12 +343,12 @@ func TestCreateRunnerScaleSet(t *testing.T) { func TestUpdateRunnerScaleSet(t *testing.T) { ctx := context.Background() - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } scaleSetCreationDateTime := time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC) - runnerScaleSet := scaleset.RunnerScaleSet{Id: 1, Name: "ScaleSet", RunnerGroupId: 1, RunnerGroupName: "group", CreatedOn: scaleSetCreationDateTime, RunnerSetting: scaleset.RunnerSetting{}} + runnerScaleSet := RunnerScaleSet{ID: 1, Name: "ScaleSet", RunnerGroupID: 1, RunnerGroupName: "group", CreatedOn: scaleSetCreationDateTime, RunnerSetting: RunnerSetting{}} t.Run("Update runner scale set", func(t *testing.T) { want := &runnerScaleSet @@ -359,10 +358,10 @@ func TestUpdateRunnerScaleSet(t *testing.T) { w.Write(rsl) })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) - got, err := client.UpdateRunnerScaleSet(ctx, 1, &scaleset.RunnerScaleSet{RunnerGroupId: 1}) + got, err := client.UpdateRunnerScaleSet(ctx, 1, &RunnerScaleSet{RunnerGroupID: 1}) require.NoError(t, err) assert.Equal(t, want, got) }) @@ -379,7 +378,7 @@ func TestUpdateRunnerScaleSet(t *testing.T) { w.Write(rsl) })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) _, err = client.UpdateRunnerScaleSet(ctx, 1, &runnerScaleSet) @@ -389,7 +388,7 @@ func TestUpdateRunnerScaleSet(t *testing.T) { func TestDeleteRunnerScaleSet(t *testing.T) { ctx := context.Background() - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } @@ -400,7 +399,7 @@ func TestDeleteRunnerScaleSet(t *testing.T) { w.WriteHeader(http.StatusNoContent) })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) err = client.DeleteRunnerScaleSet(ctx, 10) @@ -415,7 +414,7 @@ func TestDeleteRunnerScaleSet(t *testing.T) { w.Write([]byte(`{"message": "test error"}`)) })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) err = client.DeleteRunnerScaleSet(ctx, 10) diff --git a/client_runner_test.go b/client_runner_test.go index 7f819a8..120aca4 100644 --- a/client_runner_test.go +++ b/client_runner_test.go @@ -1,4 +1,4 @@ -package scaleset_test +package scaleset import ( "context" @@ -6,21 +6,20 @@ import ( "testing" "time" - "github.com/actions/scaleset" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestGetRunner(t *testing.T) { ctx := context.Background() - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } t.Run("Get Runner", func(t *testing.T) { var runnerID int64 = 1 - want := &scaleset.RunnerReference{ - Id: int(runnerID), + want := &RunnerReference{ + ID: int(runnerID), Name: "self-hosted-ubuntu", } response := []byte(`{"id": 1, "name": "self-hosted-ubuntu"}`) @@ -29,7 +28,7 @@ func TestGetRunner(t *testing.T) { w.Write(response) })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.GetRunner(ctx, runnerID) @@ -50,7 +49,7 @@ func TestGetRunner(t *testing.T) { actualRetry++ })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth, scaleset.WithRetryMax(retryMax), scaleset.WithRetryWaitMax(retryWaitMax)) + client, err := NewClient(server.configURLForOrg("my-org"), auth, WithRetryMax(retryMax), WithRetryWaitMax(retryWaitMax)) require.NoError(t, err) _, err = client.GetRunner(ctx, runnerID) @@ -61,15 +60,15 @@ func TestGetRunner(t *testing.T) { func TestGetRunnerByName(t *testing.T) { ctx := context.Background() - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } t.Run("Get Runner by Name", func(t *testing.T) { var runnerID int64 = 1 var runnerName = "self-hosted-ubuntu" - want := &scaleset.RunnerReference{ - Id: int(runnerID), + want := &RunnerReference{ + ID: int(runnerID), Name: runnerName, } response := []byte(`{"count": 1, "value": [{"id": 1, "name": "self-hosted-ubuntu"}]}`) @@ -78,7 +77,7 @@ func TestGetRunnerByName(t *testing.T) { w.Write(response) })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.GetRunnerByName(ctx, runnerName) @@ -94,7 +93,7 @@ func TestGetRunnerByName(t *testing.T) { w.Write(response) })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.GetRunnerByName(ctx, runnerName) @@ -116,7 +115,7 @@ func TestGetRunnerByName(t *testing.T) { actualRetry++ })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth, scaleset.WithRetryMax(retryMax), scaleset.WithRetryWaitMax(retryWaitMax)) + client, err := NewClient(server.configURLForOrg("my-org"), auth, WithRetryMax(retryMax), WithRetryWaitMax(retryWaitMax)) require.NoError(t, err) _, err = client.GetRunnerByName(ctx, runnerName) @@ -127,7 +126,7 @@ func TestGetRunnerByName(t *testing.T) { func TestDeleteRunner(t *testing.T) { ctx := context.Background() - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } @@ -138,7 +137,7 @@ func TestDeleteRunner(t *testing.T) { w.WriteHeader(http.StatusNoContent) })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) err = client.RemoveRunner(ctx, runnerID) @@ -159,11 +158,11 @@ func TestDeleteRunner(t *testing.T) { actualRetry++ })) - client, err := scaleset.NewClient( + client, err := NewClient( server.configURLForOrg("my-org"), auth, - scaleset.WithRetryMax(retryMax), - scaleset.WithRetryWaitMax(retryWaitMax), + WithRetryMax(retryMax), + WithRetryWaitMax(retryWaitMax), ) require.NoError(t, err) @@ -175,14 +174,14 @@ func TestDeleteRunner(t *testing.T) { func TestGetRunnerGroupByName(t *testing.T) { ctx := context.Background() - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } t.Run("Get RunnerGroup by Name", func(t *testing.T) { var runnerGroupID int64 = 1 var runnerGroupName = "test-runner-group" - want := &scaleset.RunnerGroup{ + want := &RunnerGroup{ ID: runnerGroupID, Name: runnerGroupName, } @@ -192,7 +191,7 @@ func TestGetRunnerGroupByName(t *testing.T) { w.Write(response) })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.GetRunnerGroupByName(ctx, runnerGroupName) @@ -208,7 +207,7 @@ func TestGetRunnerGroupByName(t *testing.T) { w.Write(response) })) - client, err := scaleset.NewClient(server.configURLForOrg("my-org"), auth) + client, err := NewClient(server.configURLForOrg("my-org"), auth) require.NoError(t, err) got, err := client.GetRunnerGroupByName(ctx, runnerGroupName) diff --git a/client_tls_test.go b/client_tls_test.go index 18de8fb..95ada82 100644 --- a/client_tls_test.go +++ b/client_tls_test.go @@ -1,4 +1,4 @@ -package scaleset_test +package scaleset import ( "context" @@ -14,7 +14,6 @@ import ( "testing" "time" - "github.com/actions/scaleset" "github.com/golang-jwt/jwt/v4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -62,10 +61,10 @@ func TestServerWithSelfSignedCertificates(t *testing.T) { u = server.URL configURL := server.URL + "/my-org" - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } - client, err := scaleset.NewClient(configURL, auth) + client, err := NewClient(configURL, auth) require.NoError(t, err) require.NotNil(t, client) @@ -93,7 +92,7 @@ func TestServerWithSelfSignedCertificates(t *testing.T) { u = server.URL configURL := server.URL + "/my-org" - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } @@ -103,10 +102,10 @@ func TestServerWithSelfSignedCertificates(t *testing.T) { pool := x509.NewCertPool() require.True(t, pool.AppendCertsFromPEM(cert)) - client, err := scaleset.NewClient( + client, err := NewClient( configURL, auth, - scaleset.WithRootCAs(pool), + WithRootCAs(pool), ) require.NoError(t, err) assert.NotNil(t, client) @@ -125,7 +124,7 @@ func TestServerWithSelfSignedCertificates(t *testing.T) { u = server.URL configURL := server.URL + "/my-org" - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } @@ -135,11 +134,11 @@ func TestServerWithSelfSignedCertificates(t *testing.T) { pool := x509.NewCertPool() require.True(t, pool.AppendCertsFromPEM(cert)) - client, err := scaleset.NewClient( + client, err := NewClient( configURL, auth, - scaleset.WithRootCAs(pool), - scaleset.WithRetryMax(0), + WithRootCAs(pool), + WithRetryMax(0), ) require.NoError(t, err) require.NotNil(t, client) @@ -152,11 +151,11 @@ func TestServerWithSelfSignedCertificates(t *testing.T) { server := startNewTLSTestServer(t, certPath, keyPath, http.HandlerFunc(h)) configURL := server.URL + "/my-org" - auth := &scaleset.ActionsAuth{ + auth := &ActionsAuth{ Token: "token", } - client, err := scaleset.NewClient(configURL, auth, scaleset.WithoutTLSVerify()) + client, err := NewClient(configURL, auth, WithoutTLSVerify()) require.NoError(t, err) assert.NotNil(t, client) }) diff --git a/config_test.go b/config_test.go index 190a1eb..3f6c2ff 100644 --- a/config_test.go +++ b/config_test.go @@ -1,4 +1,4 @@ -package scaleset_test +package scaleset import ( "errors" @@ -7,7 +7,6 @@ import ( "strings" "testing" - "github.com/actions/scaleset" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -16,12 +15,12 @@ func TestGitHubConfig(t *testing.T) { t.Run("when given a valid URL", func(t *testing.T) { tests := []struct { configURL string - expected *scaleset.GitHubConfig + expected *GitHubConfig }{ { configURL: "https://github.com/org/repo", - expected: &scaleset.GitHubConfig{ - Scope: scaleset.GitHubScopeRepository, + expected: &GitHubConfig{ + Scope: GitHubScopeRepository, Enterprise: "", Organization: "org", Repository: "repo", @@ -30,8 +29,8 @@ func TestGitHubConfig(t *testing.T) { }, { configURL: "https://github.com/org/repo/", - expected: &scaleset.GitHubConfig{ - Scope: scaleset.GitHubScopeRepository, + expected: &GitHubConfig{ + Scope: GitHubScopeRepository, Enterprise: "", Organization: "org", Repository: "repo", @@ -40,8 +39,8 @@ func TestGitHubConfig(t *testing.T) { }, { configURL: "https://github.com/org", - expected: &scaleset.GitHubConfig{ - Scope: scaleset.GitHubScopeOrganization, + expected: &GitHubConfig{ + Scope: GitHubScopeOrganization, Enterprise: "", Organization: "org", Repository: "", @@ -50,8 +49,8 @@ func TestGitHubConfig(t *testing.T) { }, { configURL: "https://github.com/enterprises/my-enterprise", - expected: &scaleset.GitHubConfig{ - Scope: scaleset.GitHubScopeEnterprise, + expected: &GitHubConfig{ + Scope: GitHubScopeEnterprise, Enterprise: "my-enterprise", Organization: "", Repository: "", @@ -60,8 +59,8 @@ func TestGitHubConfig(t *testing.T) { }, { configURL: "https://github.com/enterprises/my-enterprise/", - expected: &scaleset.GitHubConfig{ - Scope: scaleset.GitHubScopeEnterprise, + expected: &GitHubConfig{ + Scope: GitHubScopeEnterprise, Enterprise: "my-enterprise", Organization: "", Repository: "", @@ -70,8 +69,8 @@ func TestGitHubConfig(t *testing.T) { }, { configURL: "https://www.github.com/org", - expected: &scaleset.GitHubConfig{ - Scope: scaleset.GitHubScopeOrganization, + expected: &GitHubConfig{ + Scope: GitHubScopeOrganization, Enterprise: "", Organization: "org", Repository: "", @@ -80,8 +79,8 @@ func TestGitHubConfig(t *testing.T) { }, { configURL: "https://www.github.com/org/", - expected: &scaleset.GitHubConfig{ - Scope: scaleset.GitHubScopeOrganization, + expected: &GitHubConfig{ + Scope: GitHubScopeOrganization, Enterprise: "", Organization: "org", Repository: "", @@ -90,8 +89,8 @@ func TestGitHubConfig(t *testing.T) { }, { configURL: "https://github.localhost/org", - expected: &scaleset.GitHubConfig{ - Scope: scaleset.GitHubScopeOrganization, + expected: &GitHubConfig{ + Scope: GitHubScopeOrganization, Enterprise: "", Organization: "org", Repository: "", @@ -100,8 +99,8 @@ func TestGitHubConfig(t *testing.T) { }, { configURL: "https://my-ghes.com/org", - expected: &scaleset.GitHubConfig{ - Scope: scaleset.GitHubScopeOrganization, + expected: &GitHubConfig{ + Scope: GitHubScopeOrganization, Enterprise: "", Organization: "org", Repository: "", @@ -110,8 +109,8 @@ func TestGitHubConfig(t *testing.T) { }, { configURL: "https://my-ghes.com/org/", - expected: &scaleset.GitHubConfig{ - Scope: scaleset.GitHubScopeOrganization, + expected: &GitHubConfig{ + Scope: GitHubScopeOrganization, Enterprise: "", Organization: "org", Repository: "", @@ -120,8 +119,8 @@ func TestGitHubConfig(t *testing.T) { }, { configURL: "https://my-ghes.ghe.com/org/", - expected: &scaleset.GitHubConfig{ - Scope: scaleset.GitHubScopeOrganization, + expected: &GitHubConfig{ + Scope: GitHubScopeOrganization, Enterprise: "", Organization: "org", Repository: "", @@ -136,7 +135,7 @@ func TestGitHubConfig(t *testing.T) { require.NoError(t, err) test.expected.ConfigURL = parsedURL - cfg, err := scaleset.ParseGitHubConfigFromURL(test.configURL) + cfg, err := ParseGitHubConfigFromURL(test.configURL) require.NoError(t, err) assert.Equal(t, test.expected, cfg) }) @@ -151,16 +150,16 @@ func TestGitHubConfig(t *testing.T) { } for _, u := range invalidURLs { - _, err := scaleset.ParseGitHubConfigFromURL(u) + _, err := ParseGitHubConfigFromURL(u) require.Error(t, err) - assert.True(t, errors.Is(err, scaleset.ErrInvalidGitHubConfigURL)) + assert.True(t, errors.Is(err, ErrInvalidGitHubConfigURL)) } }) } func TestGitHubConfig_GitHubAPIURL(t *testing.T) { t.Run("when hosted", func(t *testing.T) { - config, err := scaleset.ParseGitHubConfigFromURL("https://github.com/org/repo") + config, err := ParseGitHubConfigFromURL("https://github.com/org/repo") require.NoError(t, err) assert.True(t, config.IsHosted) @@ -168,7 +167,7 @@ func TestGitHubConfig_GitHubAPIURL(t *testing.T) { assert.Equal(t, "https://api.github.com/some/path", result.String()) }) t.Run("when hosted with ghe.com", func(t *testing.T) { - config, err := scaleset.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) @@ -176,7 +175,7 @@ func TestGitHubConfig_GitHubAPIURL(t *testing.T) { assert.Equal(t, "https://api.github.ghe.com/some/path", result.String()) }) t.Run("when not hosted", func(t *testing.T) { - config, err := scaleset.ParseGitHubConfigFromURL("https://ghes.com/org/repo") + config, err := ParseGitHubConfigFromURL("https://ghes.com/org/repo") require.NoError(t, err) assert.False(t, config.IsHosted) @@ -186,7 +185,7 @@ func TestGitHubConfig_GitHubAPIURL(t *testing.T) { 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 := scaleset.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) diff --git a/errors.go b/error.go similarity index 83% rename from errors.go rename to error.go index ed12bcc..062fd0b 100644 --- a/errors.go +++ b/error.go @@ -11,8 +11,8 @@ import ( // Header names for request IDs const ( - HeaderActionsActivityID = "ActivityId" - HeaderGitHubRequestID = "X-GitHub-Request-Id" + headerActionsActivityID = "ActivityId" + headerGitHubRequestID = "X-GitHub-Request-Id" ) type GitHubAPIError struct { @@ -59,20 +59,19 @@ func (e *ActionsExceptionError) Error() string { return fmt.Sprintf("%s: %s", e.ExceptionName, e.Message) } -func ParseActionsErrorFromResponse(response *http.Response) error { +func parseActionsErrorFromResponse(response *http.Response) error { if response.ContentLength == 0 { return &ActionsError{ - ActivityID: response.Header.Get(HeaderActionsActivityID), + ActivityID: response.Header.Get(headerActionsActivityID), StatusCode: response.StatusCode, Err: errors.New("unknown exception"), } } - defer response.Body.Close() body, err := io.ReadAll(response.Body) if err != nil { return &ActionsError{ - ActivityID: response.Header.Get(HeaderActionsActivityID), + ActivityID: response.Header.Get(headerActionsActivityID), StatusCode: response.StatusCode, Err: err, } @@ -83,7 +82,7 @@ func ParseActionsErrorFromResponse(response *http.Response) error { if ok && len(contentType) > 0 && strings.Contains(contentType[0], "text/plain") { message := string(body) return &ActionsError{ - ActivityID: response.Header.Get(HeaderActionsActivityID), + ActivityID: response.Header.Get(headerActionsActivityID), StatusCode: response.StatusCode, Err: errors.New(message), } @@ -92,14 +91,14 @@ func ParseActionsErrorFromResponse(response *http.Response) error { var exception ActionsExceptionError if err := json.Unmarshal(body, &exception); err != nil { return &ActionsError{ - ActivityID: response.Header.Get(HeaderActionsActivityID), + ActivityID: response.Header.Get(headerActionsActivityID), StatusCode: response.StatusCode, Err: err, } } return &ActionsError{ - ActivityID: response.Header.Get(HeaderActionsActivityID), + ActivityID: response.Header.Get(headerActionsActivityID), StatusCode: response.StatusCode, Err: &exception, } diff --git a/errors_test.go b/errors_test.go index 5314c2d..eb31a69 100644 --- a/errors_test.go +++ b/errors_test.go @@ -1,4 +1,4 @@ -package scaleset_test +package scaleset import ( "errors" @@ -7,14 +7,13 @@ import ( "strings" "testing" - "github.com/actions/scaleset" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestActionsError(t *testing.T) { t.Run("contains the status code, activity ID, and error", func(t *testing.T) { - err := &scaleset.ActionsError{ + err := &ActionsError{ ActivityID: "activity-id", StatusCode: 404, Err: errors.New("example error description"), @@ -27,10 +26,10 @@ func TestActionsError(t *testing.T) { }) t.Run("unwraps the error", func(t *testing.T) { - err := &scaleset.ActionsError{ + err := &ActionsError{ ActivityID: "activity-id", StatusCode: 404, - Err: &scaleset.ActionsExceptionError{ + Err: &ActionsExceptionError{ ExceptionName: "exception-name", Message: "example error message", }, @@ -40,23 +39,23 @@ func TestActionsError(t *testing.T) { }) t.Run("is exception is ok", func(t *testing.T) { - err := &scaleset.ActionsError{ + err := &ActionsError{ ActivityID: "activity-id", StatusCode: 404, - Err: &scaleset.ActionsExceptionError{ + Err: &ActionsExceptionError{ ExceptionName: "exception-name", Message: "example error message", }, } - var exception *scaleset.ActionsExceptionError + var exception *ActionsExceptionError assert.True(t, errors.As(err, &exception)) assert.True(t, err.IsException("exception-name")) }) t.Run("is exception is not ok", func(t *testing.T) { - tt := map[string]*scaleset.ActionsError{ + tt := map[string]*ActionsError{ "not an exception": { ActivityID: "activity-id", StatusCode: 404, @@ -65,7 +64,7 @@ func TestActionsError(t *testing.T) { "not target exception": { ActivityID: "activity-id", StatusCode: 404, - Err: &scaleset.ActionsExceptionError{ + Err: &ActionsExceptionError{ ExceptionName: "exception-name", Message: "example error message", }, @@ -83,7 +82,7 @@ func TestActionsError(t *testing.T) { func TestActionsExceptionError(t *testing.T) { t.Run("contains the exception name and message", func(t *testing.T) { - err := &scaleset.ActionsExceptionError{ + err := &ActionsExceptionError{ ExceptionName: "exception-name", Message: "example error message", } @@ -96,7 +95,7 @@ func TestActionsExceptionError(t *testing.T) { func TestGitHubAPIError(t *testing.T) { t.Run("contains the status code, request ID, and error", func(t *testing.T) { - err := &scaleset.GitHubAPIError{ + err := &GitHubAPIError{ StatusCode: 404, RequestID: "request-id", Err: errors.New("example error description"), @@ -109,7 +108,7 @@ func TestGitHubAPIError(t *testing.T) { }) t.Run("unwraps the error", func(t *testing.T) { - err := &scaleset.GitHubAPIError{ + err := &GitHubAPIError{ StatusCode: 404, RequestID: "request-id", Err: errors.New("example error description"), @@ -124,16 +123,16 @@ func ParseActionsErrorFromResponse(t *testing.T) { response := &http.Response{ ContentLength: 0, Header: http.Header{ - scaleset.HeaderActionsActivityID: []string{"activity-id"}, + headerActionsActivityID: []string{"activity-id"}, }, StatusCode: 404, } - err := scaleset.ParseActionsErrorFromResponse(response) + err := parseActionsErrorFromResponse(response) require.Error(t, err) - assert.Equal(t, err.(*scaleset.ActionsError).ActivityID, "activity-id") - assert.Equal(t, err.(*scaleset.ActionsError).StatusCode, 404) - assert.Equal(t, err.(*scaleset.ActionsError).Err.Error(), "unknown exception") + assert.Equal(t, err.(*ActionsError).ActivityID, "activity-id") + assert.Equal(t, err.(*ActionsError).StatusCode, 404) + assert.Equal(t, err.(*ActionsError).Err.Error(), "unknown exception") }) t.Run("contains text plain error", func(t *testing.T) { @@ -141,16 +140,16 @@ func ParseActionsErrorFromResponse(t *testing.T) { response := &http.Response{ ContentLength: int64(len(errorMessage)), Header: http.Header{ - scaleset.HeaderActionsActivityID: []string{"activity-id"}, - "Content-Type": []string{"text/plain"}, + headerActionsActivityID: []string{"activity-id"}, + "Content-Type": []string{"text/plain"}, }, StatusCode: 404, Body: io.NopCloser(strings.NewReader(errorMessage)), } - err := scaleset.ParseActionsErrorFromResponse(response) + err := parseActionsErrorFromResponse(response) require.Error(t, err) - var actionsError *scaleset.ActionsError + var actionsError *ActionsError assert.ErrorAs(t, err, &actionsError) assert.Equal(t, actionsError.ActivityID, "activity-id") assert.Equal(t, actionsError.StatusCode, 404) @@ -162,21 +161,21 @@ func ParseActionsErrorFromResponse(t *testing.T) { response := &http.Response{ ContentLength: int64(len(errorMessage)), Header: http.Header{ - scaleset.HeaderActionsActivityID: []string{"activity-id"}, - "Content-Type": []string{"application/json"}, + headerActionsActivityID: []string{"activity-id"}, + "Content-Type": []string{"application/json"}, }, StatusCode: 404, Body: io.NopCloser(strings.NewReader(errorMessage)), } - err := scaleset.ParseActionsErrorFromResponse(response) + err := parseActionsErrorFromResponse(response) require.Error(t, err) - var actionsError *scaleset.ActionsError + var actionsError *ActionsError assert.ErrorAs(t, err, &actionsError) assert.Equal(t, actionsError.ActivityID, "activity-id") assert.Equal(t, actionsError.StatusCode, 404) - inner, ok := actionsError.Err.(*scaleset.ActionsExceptionError) + inner, ok := actionsError.Err.(*ActionsExceptionError) require.True(t, ok) assert.Equal(t, inner.ExceptionName, "exception-name") assert.Equal(t, inner.Message, "example error message") @@ -187,17 +186,17 @@ func ParseActionsErrorFromResponse(t *testing.T) { response := &http.Response{ ContentLength: int64(len(errorMessage)), Header: http.Header{ - scaleset.HeaderActionsActivityID: []string{"activity-id"}, - "Content-Type": []string{"application/json"}, + headerActionsActivityID: []string{"activity-id"}, + "Content-Type": []string{"application/json"}, }, StatusCode: 404, Body: io.NopCloser(strings.NewReader(errorMessage)), } - err := scaleset.ParseActionsErrorFromResponse(response) + err := parseActionsErrorFromResponse(response) require.Error(t, err) - var actionsExceptionError *scaleset.ActionsExceptionError + var actionsExceptionError *ActionsExceptionError assert.ErrorAs(t, err, &actionsExceptionError) assert.Equal(t, actionsExceptionError.ExceptionName, "exception-name") diff --git a/github_api_request_test.go b/github_api_request_test.go index 1032a70..93fe778 100644 --- a/github_api_request_test.go +++ b/github_api_request_test.go @@ -1,4 +1,4 @@ -package scaleset_test +package scaleset import ( "context" @@ -10,13 +10,12 @@ import ( "testing" "time" - "github.com/actions/scaleset" "github.com/actions/scaleset/testserver" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -var testUserAgent = scaleset.UserAgentInfo{ +var testUserAgent = UserAgentInfo{ Version: "test", CommitSHA: "test", ScaleSetID: 1, @@ -59,32 +58,32 @@ func TestNewGitHubAPIRequest(t *testing.T) { } for _, scenario := range scenarios { - client, err := scaleset.NewClient(scenario.configURL, nil) + client, err := NewClient(scenario.configURL, nil) require.NoError(t, err) - req, err := client.NewGitHubAPIRequest(ctx, http.MethodGet, scenario.path, nil) + req, err := client.newGitHubAPIRequest(ctx, http.MethodGet, scenario.path, nil) require.NoError(t, err) assert.Equal(t, scenario.expected, req.URL.String()) } }) t.Run("sets user agent header if present", func(t *testing.T) { - client, err := scaleset.NewClient("http://localhost/my-org", nil) + client, err := NewClient("http://localhost/my-org", nil) require.NoError(t, err) client.SetUserAgent(testUserAgent) - req, err := client.NewGitHubAPIRequest(ctx, http.MethodGet, "/app/installations/123/access_tokens", nil) + req, err := client.newGitHubAPIRequest(ctx, http.MethodGet, "/app/installations/123/access_tokens", nil) require.NoError(t, err) assert.Equal(t, testUserAgent.String(), req.Header.Get("User-Agent")) }) t.Run("sets the body we pass", func(t *testing.T) { - client, err := scaleset.NewClient("http://localhost/my-org", nil) + client, err := NewClient("http://localhost/my-org", nil) require.NoError(t, err) - req, err := client.NewGitHubAPIRequest( + req, err := client.newGitHubAPIRequest( ctx, http.MethodGet, "/app/installations/123/access_tokens", @@ -100,17 +99,17 @@ func TestNewGitHubAPIRequest(t *testing.T) { func TestNewActionsServiceRequest(t *testing.T) { ctx := context.Background() - defaultCreds := &scaleset.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 := scaleset.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) + req, err := client.newActionsServiceRequest(ctx, http.MethodGet, "my-path", nil) require.NoError(t, err) assert.Equal(t, "Bearer "+token, req.Header.Get("Authorization")) @@ -120,12 +119,12 @@ func TestNewActionsServiceRequest(t *testing.T) { newToken := defaultActionsToken(t) server := testserver.New(t, nil, testserver.WithActionsToken(newToken)) - client, err := scaleset.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) + client.actionsServiceAdminToken = "expiring-token" + client.actionsServiceAdminTokenExpiresAt = time.Now().Add(59 * time.Second) - req, err := client.NewActionsServiceRequest(ctx, http.MethodGet, "my-path", nil) + req, err := client.newActionsServiceRequest(ctx, http.MethodGet, "my-path", nil) require.NoError(t, err) assert.Equal(t, "Bearer "+newToken, req.Header.Get("Authorization")) @@ -146,17 +145,17 @@ func TestNewActionsServiceRequest(t *testing.T) { testserver.WithActionsToken(newToken), testserver.WithActionsRegistrationTokenHandler(unauthorizedHandler), ) - client, err := scaleset.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) - client.ActionsServiceAdminToken = expiringToken - client.ActionsServiceAdminTokenExpiresAt = expiresAt - _, err = client.NewActionsServiceRequest(ctx, http.MethodGet, "my-path", nil) + client.actionsServiceAdminToken = expiringToken + client.actionsServiceAdminTokenExpiresAt = expiresAt + _, err = client.newActionsServiceRequest(ctx, http.MethodGet, "my-path", nil) require.Error(t, err) assert.Contains(t, err.Error(), errMessage) - assert.Equal(t, client.ActionsServiceAdminToken, expiringToken) - assert.Equal(t, client.ActionsServiceAdminTokenExpiresAt, expiresAt) + assert.Equal(t, client.actionsServiceAdminToken, expiringToken) + assert.Equal(t, client.actionsServiceAdminTokenExpiresAt, expiresAt) }) t.Run("admin token refresh retry", func(t *testing.T) { @@ -164,9 +163,9 @@ func TestNewActionsServiceRequest(t *testing.T) { errMessage := `{"message":"test"}` srv := "http://github.com/my-org" - resp := &scaleset.ActionsServiceAdminConnection{ + resp := &ActionsServiceAdminConnection{ AdminToken: &newToken, - ActionsServiceUrl: &srv, + ActionsServiceURL: &srv, } failures := 0 unauthorizedHandler := func(w http.ResponseWriter, r *http.Request) { @@ -182,30 +181,30 @@ 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 := scaleset.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) - client.ActionsServiceAdminToken = expiringToken - client.ActionsServiceAdminTokenExpiresAt = expiresAt + client.actionsServiceAdminToken = expiringToken + client.actionsServiceAdminTokenExpiresAt = expiresAt - _, err = client.NewActionsServiceRequest(ctx, http.MethodGet, "my-path", nil) + _, err = client.newActionsServiceRequest(ctx, http.MethodGet, "my-path", nil) require.NoError(t, err) - assert.Equal(t, client.ActionsServiceAdminToken, newToken) - assert.Equal(t, client.ActionsServiceURL, srv) - assert.NotEqual(t, client.ActionsServiceAdminTokenExpiresAt, expiresAt) + assert.Equal(t, client.actionsServiceAdminToken, newToken) + assert.Equal(t, client.actionsServiceURL, srv) + assert.NotEqual(t, client.actionsServiceAdminTokenExpiresAt, expiresAt) }) t.Run("token is currently valid", func(t *testing.T) { tokenThatShouldNotBeFetched := defaultActionsToken(t) server := testserver.New(t, nil, testserver.WithActionsToken(tokenThatShouldNotBeFetched)) - client, err := scaleset.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) + client.actionsServiceAdminToken = "healthy-token" + client.actionsServiceAdminTokenExpiresAt = time.Now().Add(1 * time.Hour) - req, err := client.NewActionsServiceRequest(ctx, http.MethodGet, "my-path", nil) + req, err := client.newActionsServiceRequest(ctx, http.MethodGet, "my-path", nil) require.NoError(t, err) assert.Equal(t, "Bearer healthy-token", req.Header.Get("Authorization")) @@ -215,10 +214,10 @@ 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 := scaleset.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) + req, err := client.newActionsServiceRequest(ctx, http.MethodGet, "/my/path?name=banana", nil) require.NoError(t, err) serverURL, err := url.Parse(server.URL) @@ -234,12 +233,12 @@ func TestNewActionsServiceRequest(t *testing.T) { t.Run("populates header", func(t *testing.T) { server := testserver.New(t, nil) - client, err := scaleset.NewClient(server.ConfigURLForOrg("my-org"), defaultCreds) + client, err := NewClient(server.ConfigURLForOrg("my-org"), defaultCreds) require.NoError(t, err) client.SetUserAgent(testUserAgent) - req, err := client.NewActionsServiceRequest(ctx, http.MethodGet, "/my/path", nil) + req, err := client.newActionsServiceRequest(ctx, http.MethodGet, "/my/path", nil) require.NoError(t, err) assert.Equal(t, testUserAgent.String(), req.Header.Get("User-Agent")) diff --git a/types.go b/types.go index 1852b84..1222f1c 100644 --- a/types.go +++ b/types.go @@ -12,9 +12,9 @@ type AcquirableJobList struct { } type AcquirableJob struct { - AcquireJobUrl string `json:"acquireJobUrl"` + AcquireJobURL string `json:"acquireJobUrl"` MessageType string `json:"messageType"` - RunnerRequestId int64 `json:"runnerRequestId"` + RunnerRequestID int64 `json:"runnerRequestId"` RepositoryName string `json:"repositoryName"` OwnerName string `json:"ownerName"` JobWorkflowRef string `json:"jobWorkflowRef"` @@ -28,7 +28,7 @@ type Int64List struct { } type JobAvailable struct { - AcquireJobUrl string `json:"acquireJobUrl"` + AcquireJobURL string `json:"acquireJobUrl"` JobMessageBase } @@ -44,7 +44,7 @@ type JobStarted struct { type JobCompleted struct { Result string `json:"result"` - RunnerId int `json:"runnerId"` + RunnerID int `json:"runnerId"` RunnerName string `json:"runnerName"` JobMessageBase } @@ -88,14 +88,14 @@ type RunnerGroupList struct { } type RunnerScaleSet struct { - Id int `json:"id,omitempty"` + ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` - RunnerGroupId int `json:"runnerGroupId,omitempty"` + RunnerGroupID int `json:"runnerGroupId,omitempty"` RunnerGroupName string `json:"runnerGroupName,omitempty"` Labels []Label `json:"labels,omitempty"` RunnerSetting RunnerSetting `json:"RunnerSetting,omitempty"` CreatedOn time.Time `json:"createdOn,omitempty"` - RunnerJitConfigUrl string `json:"runnerJitConfigUrl,omitempty"` + RunnerJitConfigURL string `json:"runnerJitConfigUrl,omitempty"` Statistics *RunnerScaleSetStatistic `json:"statistics,omitempty"` } @@ -105,7 +105,7 @@ type RunnerScaleSetJitRunnerSetting struct { } type RunnerScaleSetMessage struct { - MessageId int64 `json:"messageId"` + MessageID int64 `json:"messageId"` MessageType string `json:"messageType"` Body string `json:"body"` Statistics *RunnerScaleSetStatistic `json:"statistics"` @@ -117,10 +117,10 @@ type runnerScaleSetsResponse struct { } type RunnerScaleSetSession struct { - SessionId *uuid.UUID `json:"sessionId,omitempty"` + SessionID *uuid.UUID `json:"sessionId,omitempty"` OwnerName string `json:"ownerName,omitempty"` RunnerScaleSet *RunnerScaleSet `json:"runnerScaleSet,omitempty"` - MessageQueueUrl string `json:"messageQueueUrl,omitempty"` + MessageQueueURL string `json:"messageQueueUrl,omitempty"` MessageQueueAccessToken string `json:"messageQueueAccessToken,omitempty"` Statistics *RunnerScaleSetStatistic `json:"statistics,omitempty"` } @@ -147,9 +147,9 @@ type RunnerReferenceList struct { } type RunnerReference struct { - Id int `json:"id"` + ID int `json:"id"` Name string `json:"name"` - RunnerScaleSetId int `json:"runnerScaleSetId"` + RunnerScaleSetID int `json:"runnerScaleSetId"` } type RunnerScaleSetJitRunnerConfig struct { diff --git a/user_agent_test.go b/user_agent_test.go index 1f0555f..4096299 100644 --- a/user_agent_test.go +++ b/user_agent_test.go @@ -1,14 +1,14 @@ -package scaleset_test +package scaleset import ( "testing" - "github.com/actions/scaleset" "github.com/stretchr/testify/assert" ) func TestUserAgentInfoString(t *testing.T) { - userAgentInfo := scaleset.UserAgentInfo{ + userAgentInfo := UserAgentInfo{ + System: "actions-runner-controller", Version: "0.1.0", CommitSHA: "1234567890abcdef", ScaleSetID: 10,