Remove network requests from actions.NewClient (#2219)
Co-authored-by: Nikola Jokic <jokicnikola07@gmail.com>
This commit is contained in:
parent
cc26593a9b
commit
df12e00c9e
|
|
@ -85,7 +85,6 @@ func run(rc RunnerScaleSetListenerConfig, logger logr.Logger) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
actionsServiceClient, err := actions.NewClient(
|
actionsServiceClient, err := actions.NewClient(
|
||||||
ctx,
|
|
||||||
rc.ConfigureUrl,
|
rc.ConfigureUrl,
|
||||||
creds,
|
creds,
|
||||||
actions.WithUserAgent(fmt.Sprintf("actions-runner-controller/%s", build.Version)),
|
actions.WithUserAgent(fmt.Sprintf("actions-runner-controller/%s", build.Version)),
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,20 @@ import (
|
||||||
// /actions/runner-registration endpoints will be handled by the provided
|
// /actions/runner-registration endpoints will be handled by the provided
|
||||||
// handler. The returned server is started and will be automatically closed
|
// handler. The returned server is started and will be automatically closed
|
||||||
// when the test ends.
|
// when the test ends.
|
||||||
func newActionsServer(t *testing.T, handler http.Handler) *actionsServer {
|
func newActionsServer(t *testing.T, handler http.Handler, options ...actionsServerOption) *actionsServer {
|
||||||
var u string
|
s := httptest.NewServer(nil)
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := &actionsServer{
|
||||||
|
Server: s,
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
server.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(server)
|
||||||
|
}
|
||||||
|
|
||||||
|
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// handle getRunnerRegistrationToken
|
// handle getRunnerRegistrationToken
|
||||||
if strings.HasSuffix(r.URL.Path, "/runners/registration-token") {
|
if strings.HasSuffix(r.URL.Path, "/runners/registration-token") {
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
|
@ -29,41 +40,55 @@ func newActionsServer(t *testing.T, handler http.Handler) *actionsServer {
|
||||||
|
|
||||||
// handle getActionsServiceAdminConnection
|
// handle getActionsServiceAdminConnection
|
||||||
if strings.HasSuffix(r.URL.Path, "/actions/runner-registration") {
|
if strings.HasSuffix(r.URL.Path, "/actions/runner-registration") {
|
||||||
claims := &jwt.RegisteredClaims{
|
if server.token == "" {
|
||||||
IssuedAt: jwt.NewNumericDate(time.Now().Add(-1 * time.Minute)),
|
server.token = defaultActionsToken(t)
|
||||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Minute)),
|
|
||||||
Issuer: "123",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
w.Write([]byte(`{"url":"` + s.URL + `/tenant/123/","token":"` + server.token + `"}`))
|
||||||
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(samplePrivateKey))
|
|
||||||
require.NoError(t, err)
|
|
||||||
tokenString, err := token.SignedString(privateKey)
|
|
||||||
require.NoError(t, err)
|
|
||||||
w.Write([]byte(`{"url":"` + u + `","token":"` + tokenString + `"}`))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.ServeHTTP(w, r)
|
handler.ServeHTTP(w, r)
|
||||||
}))
|
|
||||||
|
|
||||||
u = server.URL
|
|
||||||
|
|
||||||
t.Cleanup(func() {
|
|
||||||
server.Close()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return &actionsServer{server}
|
server.Config.Handler = h
|
||||||
|
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
|
||||||
|
type actionsServerOption func(*actionsServer)
|
||||||
|
|
||||||
|
func withActionsToken(token string) actionsServerOption {
|
||||||
|
return func(s *actionsServer) {
|
||||||
|
s.token = token
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type actionsServer struct {
|
type actionsServer struct {
|
||||||
*httptest.Server
|
*httptest.Server
|
||||||
|
|
||||||
|
token string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *actionsServer) configURLForOrg(org string) string {
|
func (s *actionsServer) configURLForOrg(org string) string {
|
||||||
return s.URL + "/" + org
|
return s.URL + "/" + org
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func defaultActionsToken(t *testing.T) string {
|
||||||
|
claims := &jwt.RegisteredClaims{
|
||||||
|
IssuedAt: jwt.NewNumericDate(time.Now().Add(-10 * time.Minute)),
|
||||||
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(10 * time.Minute)),
|
||||||
|
Issuer: "123",
|
||||||
|
}
|
||||||
|
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||||
|
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(samplePrivateKey))
|
||||||
|
require.NoError(t, err)
|
||||||
|
tokenString, err := token.SignedString(privateKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return tokenString
|
||||||
|
}
|
||||||
|
|
||||||
const samplePrivateKey = `-----BEGIN RSA PRIVATE KEY-----
|
const samplePrivateKey = `-----BEGIN RSA PRIVATE KEY-----
|
||||||
MIICWgIBAAKBgHXfRT9cv9UY9fAAD4+1RshpfSSZe277urfEmPfX3/Og9zJYRk//
|
MIICWgIBAAKBgHXfRT9cv9UY9fAAD4+1RshpfSSZe277urfEmPfX3/Og9zJYRk//
|
||||||
CZrJVD1CaBZDiIyQsNEzjta7r4UsqWdFOggiNN2E7ZTFQjMSaFkVgrzHqWuiaCBf
|
CZrJVD1CaBZDiIyQsNEzjta7r4UsqWdFOggiNN2E7ZTFQjMSaFkVgrzHqWuiaCBf
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
package actions_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/actions/actions-runner-controller/github/actions"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClient_Do(t *testing.T) {
|
||||||
|
t.Run("trims byte order mark from response if present", func(t *testing.T) {
|
||||||
|
t.Run("when there is no body", func(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
client, err := actions.NewClient("https://localhost/org/repo", &actions.ActionsAuth{Token: "token"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", server.URL, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Empty(t, string(body))
|
||||||
|
})
|
||||||
|
|
||||||
|
responses := []string{
|
||||||
|
"\xef\xbb\xbf{\"foo\":\"bar\"}",
|
||||||
|
"{\"foo\":\"bar\"}",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, response := range responses {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Write([]byte(response))
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
client, err := actions.NewClient("https://localhost/org/repo", &actions.ActionsAuth{Token: "token"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", server.URL, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "{\"foo\":\"bar\"}", string(body))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -12,9 +12,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -62,17 +60,17 @@ type Client struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
|
||||||
// TODO: Convert to unexported fields once refactor of Listener is complete
|
// TODO: Convert to unexported fields once refactor of Listener is complete
|
||||||
ActionsServiceAdminToken *string
|
ActionsServiceAdminToken string
|
||||||
ActionsServiceAdminTokenExpiresAt *time.Time
|
ActionsServiceAdminTokenExpiresAt time.Time
|
||||||
ActionsServiceURL *string
|
ActionsServiceURL string
|
||||||
|
|
||||||
retryMax int
|
retryMax int
|
||||||
retryWaitMax time.Duration
|
retryWaitMax time.Duration
|
||||||
|
|
||||||
creds *ActionsAuth
|
creds *ActionsAuth
|
||||||
githubConfigURL string
|
config *GitHubConfig
|
||||||
logger logr.Logger
|
logger logr.Logger
|
||||||
userAgent string
|
userAgent string
|
||||||
|
|
||||||
rootCAs *x509.CertPool
|
rootCAs *x509.CertPool
|
||||||
tlsInsecureSkipVerify bool
|
tlsInsecureSkipVerify bool
|
||||||
|
|
@ -116,11 +114,16 @@ func WithoutTLSVerify() ClientOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(ctx context.Context, githubConfigURL string, creds *ActionsAuth, options ...ClientOption) (ActionsService, error) {
|
func NewClient(githubConfigURL string, creds *ActionsAuth, options ...ClientOption) (*Client, error) {
|
||||||
|
config, err := ParseGitHubConfigFromURL(githubConfigURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse githubConfigURL: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
ac := &Client{
|
ac := &Client{
|
||||||
creds: creds,
|
creds: creds,
|
||||||
githubConfigURL: githubConfigURL,
|
config: config,
|
||||||
logger: logr.Discard(),
|
logger: logr.Discard(),
|
||||||
|
|
||||||
// retryablehttp defaults
|
// retryablehttp defaults
|
||||||
retryMax: 4,
|
retryMax: 4,
|
||||||
|
|
@ -132,9 +135,6 @@ func NewClient(ctx context.Context, githubConfigURL string, creds *ActionsAuth,
|
||||||
}
|
}
|
||||||
|
|
||||||
retryClient := retryablehttp.NewClient()
|
retryClient := retryablehttp.NewClient()
|
||||||
|
|
||||||
// TODO: this silences retryclient default logger, do we want to provide one
|
|
||||||
// instead? by default retryablehttp logs all requests to stderr
|
|
||||||
retryClient.Logger = log.New(io.Discard, "", log.LstdFlags)
|
retryClient.Logger = log.New(io.Discard, "", log.LstdFlags)
|
||||||
|
|
||||||
retryClient.RetryMax = ac.retryMax
|
retryClient.RetryMax = ac.retryMax
|
||||||
|
|
@ -161,48 +161,94 @@ func NewClient(ctx context.Context, githubConfigURL string, creds *ActionsAuth,
|
||||||
retryClient.HTTPClient.Transport = transport
|
retryClient.HTTPClient.Transport = transport
|
||||||
ac.Client = retryClient.StandardClient()
|
ac.Client = retryClient.StandardClient()
|
||||||
|
|
||||||
rt, err := ac.getRunnerRegistrationToken(ctx, githubConfigURL, *creds)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get runner registration token: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
adminConnInfo, err := ac.getActionsServiceAdminConnection(ctx, rt, githubConfigURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get actions service admin connection: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ac.ActionsServiceURL = adminConnInfo.ActionsServiceUrl
|
|
||||||
|
|
||||||
ac.mu.Lock()
|
|
||||||
defer ac.mu.Unlock()
|
|
||||||
ac.ActionsServiceAdminToken = adminConnInfo.AdminToken
|
|
||||||
ac.ActionsServiceAdminTokenExpiresAt, err = actionsServiceAdminTokenExpiresAt(*adminConnInfo.AdminToken)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get admin token expire at: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ac, nil
|
return ac, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetRunnerScaleSet(ctx context.Context, runnerScaleSetName string) (*RunnerScaleSet, error) {
|
func (c *Client) Do(req *http.Request) (*http.Response, error) {
|
||||||
u := fmt.Sprintf("%s/%s?name=%s&api-version=6.0-preview", *c.ActionsServiceURL, scaleSetEndpoint, runnerScaleSetName)
|
resp, err := c.Client.Do(req)
|
||||||
|
if err != nil {
|
||||||
if err := c.refreshTokenIfNeeded(ctx); err != nil {
|
return nil, err
|
||||||
return nil, fmt.Errorf("failed to refresh admin token if needed: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
body = trimByteOrderMark(body)
|
||||||
|
resp.Body = io.NopCloser(bytes.NewReader(body))
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.userAgent != "" {
|
||||||
|
req.Header.Set("User-Agent", c.userAgent)
|
||||||
|
}
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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, err
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedPath, err := url.Parse(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
urlString, err := url.JoinPath(c.ActionsServiceURL, parsedPath.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(urlString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
q := u.Query()
|
||||||
|
for k, v := range parsedPath.Query() {
|
||||||
|
q[k] = v
|
||||||
|
}
|
||||||
|
if q.Get("api-version") == "" {
|
||||||
|
q.Set("api-version", "6.0-preview")
|
||||||
|
}
|
||||||
|
u.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, method, u.String(), body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *c.ActionsServiceAdminToken))
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.ActionsServiceAdminToken))
|
||||||
|
|
||||||
if c.userAgent != "" {
|
if c.userAgent != "" {
|
||||||
req.Header.Set("User-Agent", c.userAgent)
|
req.Header.Set("User-Agent", c.userAgent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetRunnerScaleSet(ctx context.Context, runnerScaleSetName string) (*RunnerScaleSet, error) {
|
||||||
|
path := fmt.Sprintf("/%s?name=%s", scaleSetEndpoint, runnerScaleSetName)
|
||||||
|
req, err := c.NewActionsServiceRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -211,8 +257,9 @@ func (c *Client) GetRunnerScaleSet(ctx context.Context, runnerScaleSetName strin
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, ParseActionsErrorFromResponse(resp)
|
return nil, ParseActionsErrorFromResponse(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
var runnerScaleSetList *runnerScaleSetsResponse
|
var runnerScaleSetList *runnerScaleSetsResponse
|
||||||
err = unmarshalBody(resp, &runnerScaleSetList)
|
err = json.NewDecoder(resp.Body).Decode(&runnerScaleSetList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -227,24 +274,12 @@ func (c *Client) GetRunnerScaleSet(ctx context.Context, runnerScaleSetName strin
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetRunnerScaleSetById(ctx context.Context, runnerScaleSetId int) (*RunnerScaleSet, error) {
|
func (c *Client) GetRunnerScaleSetById(ctx context.Context, runnerScaleSetId int) (*RunnerScaleSet, error) {
|
||||||
u := fmt.Sprintf("%s/%s/%d?api-version=6.0-preview", *c.ActionsServiceURL, scaleSetEndpoint, runnerScaleSetId)
|
path := fmt.Sprintf("/%s/%d", scaleSetEndpoint, runnerScaleSetId)
|
||||||
|
req, err := c.NewActionsServiceRequest(ctx, http.MethodGet, path, nil)
|
||||||
if err := c.refreshTokenIfNeeded(ctx); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to refresh admin token if needed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *c.ActionsServiceAdminToken))
|
|
||||||
|
|
||||||
if c.userAgent != "" {
|
|
||||||
req.Header.Set("User-Agent", c.userAgent)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -255,7 +290,7 @@ func (c *Client) GetRunnerScaleSetById(ctx context.Context, runnerScaleSetId int
|
||||||
}
|
}
|
||||||
|
|
||||||
var runnerScaleSet *RunnerScaleSet
|
var runnerScaleSet *RunnerScaleSet
|
||||||
err = unmarshalBody(resp, &runnerScaleSet)
|
err = json.NewDecoder(resp.Body).Decode(&runnerScaleSet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -263,24 +298,12 @@ func (c *Client) GetRunnerScaleSetById(ctx context.Context, runnerScaleSetId int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetRunnerGroupByName(ctx context.Context, runnerGroup string) (*RunnerGroup, error) {
|
func (c *Client) GetRunnerGroupByName(ctx context.Context, runnerGroup string) (*RunnerGroup, error) {
|
||||||
u := fmt.Sprintf("%s/_apis/runtime/runnergroups/?groupName=%s&api-version=6.0-preview", *c.ActionsServiceURL, runnerGroup)
|
path := fmt.Sprintf("/_apis/runtime/runnergroups/?groupName=%s", runnerGroup)
|
||||||
|
req, err := c.NewActionsServiceRequest(ctx, http.MethodGet, path, nil)
|
||||||
if err := c.refreshTokenIfNeeded(ctx); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to refresh admin token if needed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *c.ActionsServiceAdminToken))
|
|
||||||
|
|
||||||
if c.userAgent != "" {
|
|
||||||
req.Header.Set("User-Agent", c.userAgent)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -295,7 +318,7 @@ func (c *Client) GetRunnerGroupByName(ctx context.Context, runnerGroup string) (
|
||||||
}
|
}
|
||||||
|
|
||||||
var runnerGroupList *RunnerGroupList
|
var runnerGroupList *RunnerGroupList
|
||||||
err = unmarshalBody(resp, &runnerGroupList)
|
err = json.NewDecoder(resp.Body).Decode(&runnerGroupList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -312,29 +335,16 @@ func (c *Client) GetRunnerGroupByName(ctx context.Context, runnerGroup string) (
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) CreateRunnerScaleSet(ctx context.Context, runnerScaleSet *RunnerScaleSet) (*RunnerScaleSet, error) {
|
func (c *Client) CreateRunnerScaleSet(ctx context.Context, runnerScaleSet *RunnerScaleSet) (*RunnerScaleSet, error) {
|
||||||
u := fmt.Sprintf("%s/%s?api-version=6.0-preview", *c.ActionsServiceURL, scaleSetEndpoint)
|
|
||||||
|
|
||||||
if err := c.refreshTokenIfNeeded(ctx); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to refresh admin token if needed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := json.Marshal(runnerScaleSet)
|
body, err := json.Marshal(runnerScaleSet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, u, bytes.NewBuffer(body))
|
req, err := c.NewActionsServiceRequest(ctx, http.MethodPost, scaleSetEndpoint, bytes.NewReader(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *c.ActionsServiceAdminToken))
|
|
||||||
|
|
||||||
if c.userAgent != "" {
|
|
||||||
req.Header.Set("User-Agent", c.userAgent)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -344,7 +354,7 @@ func (c *Client) CreateRunnerScaleSet(ctx context.Context, runnerScaleSet *Runne
|
||||||
return nil, ParseActionsErrorFromResponse(resp)
|
return nil, ParseActionsErrorFromResponse(resp)
|
||||||
}
|
}
|
||||||
var createdRunnerScaleSet *RunnerScaleSet
|
var createdRunnerScaleSet *RunnerScaleSet
|
||||||
err = unmarshalBody(resp, &createdRunnerScaleSet)
|
err = json.NewDecoder(resp.Body).Decode(&createdRunnerScaleSet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -352,29 +362,18 @@ func (c *Client) CreateRunnerScaleSet(ctx context.Context, runnerScaleSet *Runne
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) UpdateRunnerScaleSet(ctx context.Context, runnerScaleSetId int, runnerScaleSet *RunnerScaleSet) (*RunnerScaleSet, error) {
|
func (c *Client) UpdateRunnerScaleSet(ctx context.Context, runnerScaleSetId int, runnerScaleSet *RunnerScaleSet) (*RunnerScaleSet, error) {
|
||||||
u := fmt.Sprintf("%s/%s/%d?api-version=6.0-preview", *c.ActionsServiceURL, scaleSetEndpoint, runnerScaleSetId)
|
path := fmt.Sprintf("%s/%d", scaleSetEndpoint, runnerScaleSetId)
|
||||||
|
|
||||||
if err := c.refreshTokenIfNeeded(ctx); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to refresh admin token if needed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := json.Marshal(runnerScaleSet)
|
body, err := json.Marshal(runnerScaleSet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPatch, u, bytes.NewBuffer(body))
|
req, err := c.NewActionsServiceRequest(ctx, http.MethodPatch, path, bytes.NewReader(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *c.ActionsServiceAdminToken))
|
|
||||||
|
|
||||||
if c.userAgent != "" {
|
|
||||||
req.Header.Set("User-Agent", c.userAgent)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -385,7 +384,7 @@ func (c *Client) UpdateRunnerScaleSet(ctx context.Context, runnerScaleSetId int,
|
||||||
}
|
}
|
||||||
|
|
||||||
var updatedRunnerScaleSet *RunnerScaleSet
|
var updatedRunnerScaleSet *RunnerScaleSet
|
||||||
err = unmarshalBody(resp, &updatedRunnerScaleSet)
|
err = json.NewDecoder(resp.Body).Decode(&updatedRunnerScaleSet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -393,24 +392,12 @@ func (c *Client) UpdateRunnerScaleSet(ctx context.Context, runnerScaleSetId int,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DeleteRunnerScaleSet(ctx context.Context, runnerScaleSetId int) error {
|
func (c *Client) DeleteRunnerScaleSet(ctx context.Context, runnerScaleSetId int) error {
|
||||||
u := fmt.Sprintf("%s/%s/%d?api-version=6.0-preview", *c.ActionsServiceURL, scaleSetEndpoint, runnerScaleSetId)
|
path := fmt.Sprintf("/%s/%d", scaleSetEndpoint, runnerScaleSetId)
|
||||||
|
req, err := c.NewActionsServiceRequest(ctx, http.MethodDelete, path, nil)
|
||||||
if err := c.refreshTokenIfNeeded(ctx); err != nil {
|
|
||||||
return fmt.Errorf("failed to refresh admin token if needed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, u, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *c.ActionsServiceAdminToken))
|
|
||||||
|
|
||||||
if c.userAgent != "" {
|
|
||||||
req.Header.Set("User-Agent", c.userAgent)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -425,12 +412,18 @@ func (c *Client) DeleteRunnerScaleSet(ctx context.Context, runnerScaleSetId int)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetMessage(ctx context.Context, messageQueueUrl, messageQueueAccessToken string, lastMessageId int64) (*RunnerScaleSetMessage, error) {
|
func (c *Client) GetMessage(ctx context.Context, messageQueueUrl, messageQueueAccessToken string, lastMessageId int64) (*RunnerScaleSetMessage, error) {
|
||||||
u := messageQueueUrl
|
u, err := url.Parse(messageQueueUrl)
|
||||||
if lastMessageId > 0 {
|
if err != nil {
|
||||||
u = fmt.Sprintf("%s&lassMessageId=%d", messageQueueUrl, lastMessageId)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
if lastMessageId > 0 {
|
||||||
|
q := u.Query()
|
||||||
|
q.Set("lastMessageId", strconv.FormatInt(lastMessageId, 10))
|
||||||
|
u.RawQuery = q.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -466,7 +459,7 @@ func (c *Client) GetMessage(ctx context.Context, messageQueueUrl, messageQueueAc
|
||||||
}
|
}
|
||||||
|
|
||||||
var message *RunnerScaleSetMessage
|
var message *RunnerScaleSetMessage
|
||||||
err = unmarshalBody(resp, &message)
|
err = json.NewDecoder(resp.Body).Decode(&message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -514,7 +507,7 @@ func (c *Client) DeleteMessage(ctx context.Context, messageQueueUrl, messageQueu
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) CreateMessageSession(ctx context.Context, runnerScaleSetId int, owner string) (*RunnerScaleSetSession, error) {
|
func (c *Client) CreateMessageSession(ctx context.Context, runnerScaleSetId int, owner string) (*RunnerScaleSetSession, error) {
|
||||||
u := fmt.Sprintf("%v/%v/%v/sessions?%v", *c.ActionsServiceURL, scaleSetEndpoint, runnerScaleSetId, apiVersionQueryParam)
|
path := fmt.Sprintf("/%s/%d/sessions", scaleSetEndpoint, runnerScaleSetId)
|
||||||
|
|
||||||
newSession := &RunnerScaleSetSession{
|
newSession := &RunnerScaleSetSession{
|
||||||
OwnerName: owner,
|
OwnerName: owner,
|
||||||
|
|
@ -527,49 +520,36 @@ func (c *Client) CreateMessageSession(ctx context.Context, runnerScaleSetId int,
|
||||||
|
|
||||||
createdSession := &RunnerScaleSetSession{}
|
createdSession := &RunnerScaleSetSession{}
|
||||||
|
|
||||||
err = c.doSessionRequest(ctx, http.MethodPost, u, bytes.NewBuffer(requestData), http.StatusOK, createdSession)
|
err = c.doSessionRequest(ctx, http.MethodPost, path, bytes.NewBuffer(requestData), http.StatusOK, createdSession)
|
||||||
|
|
||||||
return createdSession, err
|
return createdSession, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DeleteMessageSession(ctx context.Context, runnerScaleSetId int, sessionId *uuid.UUID) error {
|
func (c *Client) DeleteMessageSession(ctx context.Context, runnerScaleSetId int, sessionId *uuid.UUID) error {
|
||||||
u := fmt.Sprintf("%v/%v/%v/sessions/%v?%v", *c.ActionsServiceURL, scaleSetEndpoint, runnerScaleSetId, sessionId.String(), apiVersionQueryParam)
|
path := fmt.Sprintf("/%s/%d/sessions/%s", scaleSetEndpoint, runnerScaleSetId, sessionId.String())
|
||||||
|
return c.doSessionRequest(ctx, http.MethodDelete, path, nil, http.StatusNoContent, nil)
|
||||||
return c.doSessionRequest(ctx, http.MethodDelete, u, nil, http.StatusNoContent, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) RefreshMessageSession(ctx context.Context, runnerScaleSetId int, sessionId *uuid.UUID) (*RunnerScaleSetSession, error) {
|
func (c *Client) RefreshMessageSession(ctx context.Context, runnerScaleSetId int, sessionId *uuid.UUID) (*RunnerScaleSetSession, error) {
|
||||||
u := fmt.Sprintf("%v/%v/%v/sessions/%v?%v", *c.ActionsServiceURL, scaleSetEndpoint, runnerScaleSetId, sessionId.String(), apiVersionQueryParam)
|
path := fmt.Sprintf("/%s/%d/sessions/%s", scaleSetEndpoint, runnerScaleSetId, sessionId.String())
|
||||||
refreshedSession := &RunnerScaleSetSession{}
|
refreshedSession := &RunnerScaleSetSession{}
|
||||||
err := c.doSessionRequest(ctx, http.MethodPatch, u, nil, http.StatusOK, refreshedSession)
|
err := c.doSessionRequest(ctx, http.MethodPatch, path, nil, http.StatusOK, refreshedSession)
|
||||||
return refreshedSession, err
|
return refreshedSession, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) doSessionRequest(ctx context.Context, method, url string, requestData io.Reader, expectedResponseStatusCode int, responseUnmarshalTarget any) error {
|
func (c *Client) doSessionRequest(ctx context.Context, method, path string, requestData io.Reader, expectedResponseStatusCode int, responseUnmarshalTarget any) error {
|
||||||
if err := c.refreshTokenIfNeeded(ctx); err != nil {
|
req, err := c.NewActionsServiceRequest(ctx, method, path, requestData)
|
||||||
return fmt.Errorf("failed to refresh admin token if needed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, method, url, requestData)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *c.ActionsServiceAdminToken))
|
|
||||||
|
|
||||||
if c.userAgent != "" {
|
|
||||||
req.Header.Set("User-Agent", c.userAgent)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode == expectedResponseStatusCode && responseUnmarshalTarget != nil {
|
if resp.StatusCode == expectedResponseStatusCode && responseUnmarshalTarget != nil {
|
||||||
err = unmarshalBody(resp, &responseUnmarshalTarget)
|
return json.NewDecoder(resp.Body).Decode(responseUnmarshalTarget)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode >= 400 && resp.StatusCode < 500 {
|
if resp.StatusCode >= 400 && resp.StatusCode < 500 {
|
||||||
|
|
@ -587,7 +567,7 @@ func (c *Client) doSessionRequest(ctx context.Context, method, url string, reque
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) AcquireJobs(ctx context.Context, runnerScaleSetId int, messageQueueAccessToken string, requestIds []int64) ([]int64, error) {
|
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)
|
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 {
|
if err != nil {
|
||||||
|
|
@ -614,8 +594,8 @@ func (c *Client) AcquireJobs(ctx context.Context, runnerScaleSetId int, messageQ
|
||||||
return nil, ParseActionsErrorFromResponse(resp)
|
return nil, ParseActionsErrorFromResponse(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
var acquiredJobs Int64List
|
var acquiredJobs *Int64List
|
||||||
err = unmarshalBody(resp, &acquiredJobs)
|
err = json.NewDecoder(resp.Body).Decode(&acquiredJobs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -624,24 +604,13 @@ func (c *Client) AcquireJobs(ctx context.Context, runnerScaleSetId int, messageQ
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetAcquirableJobs(ctx context.Context, runnerScaleSetId int) (*AcquirableJobList, error) {
|
func (c *Client) GetAcquirableJobs(ctx context.Context, runnerScaleSetId int) (*AcquirableJobList, error) {
|
||||||
u := fmt.Sprintf("%s/%s/%d/acquirablejobs?api-version=6.0-preview", *c.ActionsServiceURL, scaleSetEndpoint, runnerScaleSetId)
|
path := fmt.Sprintf("/%s/%d/acquirablejobs", scaleSetEndpoint, runnerScaleSetId)
|
||||||
|
|
||||||
if err := c.refreshTokenIfNeeded(ctx); err != nil {
|
req, err := c.NewActionsServiceRequest(ctx, http.MethodGet, path, nil)
|
||||||
return nil, fmt.Errorf("failed to refresh admin token if needed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *c.ActionsServiceAdminToken))
|
|
||||||
|
|
||||||
if c.userAgent != "" {
|
|
||||||
req.Header.Set("User-Agent", c.userAgent)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -657,7 +626,7 @@ func (c *Client) GetAcquirableJobs(ctx context.Context, runnerScaleSetId int) (*
|
||||||
}
|
}
|
||||||
|
|
||||||
var acquirableJobList *AcquirableJobList
|
var acquirableJobList *AcquirableJobList
|
||||||
err = unmarshalBody(resp, &acquirableJobList)
|
err = json.NewDecoder(resp.Body).Decode(&acquirableJobList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -666,28 +635,18 @@ func (c *Client) GetAcquirableJobs(ctx context.Context, runnerScaleSetId int) (*
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GenerateJitRunnerConfig(ctx context.Context, jitRunnerSetting *RunnerScaleSetJitRunnerSetting, scaleSetId int) (*RunnerScaleSetJitRunnerConfig, error) {
|
func (c *Client) GenerateJitRunnerConfig(ctx context.Context, jitRunnerSetting *RunnerScaleSetJitRunnerSetting, scaleSetId int) (*RunnerScaleSetJitRunnerConfig, error) {
|
||||||
runnerJitConfigUrl := fmt.Sprintf("%s/%s/%d/generatejitconfig?api-version=6.0-preview", *c.ActionsServiceURL, scaleSetEndpoint, scaleSetId)
|
path := fmt.Sprintf("/%s/%d/generatejitconfig", scaleSetEndpoint, scaleSetId)
|
||||||
|
|
||||||
if err := c.refreshTokenIfNeeded(ctx); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to refresh admin token if needed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := json.Marshal(jitRunnerSetting)
|
body, err := json.Marshal(jitRunnerSetting)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, runnerJitConfigUrl, bytes.NewBuffer(body))
|
req, err := c.NewActionsServiceRequest(ctx, http.MethodPost, path, bytes.NewBuffer(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *c.ActionsServiceAdminToken))
|
|
||||||
if c.userAgent != "" {
|
|
||||||
req.Header.Set("User-Agent", c.userAgent)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -698,7 +657,7 @@ func (c *Client) GenerateJitRunnerConfig(ctx context.Context, jitRunnerSetting *
|
||||||
}
|
}
|
||||||
|
|
||||||
var runnerJitConfig *RunnerScaleSetJitRunnerConfig
|
var runnerJitConfig *RunnerScaleSetJitRunnerConfig
|
||||||
err = unmarshalBody(resp, &runnerJitConfig)
|
err = json.NewDecoder(resp.Body).Decode(&runnerJitConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -706,24 +665,13 @@ func (c *Client) GenerateJitRunnerConfig(ctx context.Context, jitRunnerSetting *
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetRunner(ctx context.Context, runnerId int64) (*RunnerReference, error) {
|
func (c *Client) GetRunner(ctx context.Context, runnerId int64) (*RunnerReference, error) {
|
||||||
url := fmt.Sprintf("%v/%v/%v?%v", *c.ActionsServiceURL, runnerEndpoint, runnerId, apiVersionQueryParam)
|
path := fmt.Sprintf("/%s/%d", runnerEndpoint, runnerId)
|
||||||
|
|
||||||
if err := c.refreshTokenIfNeeded(ctx); err != nil {
|
req, err := c.NewActionsServiceRequest(ctx, http.MethodGet, path, nil)
|
||||||
return nil, fmt.Errorf("failed to refresh admin token if needed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *c.ActionsServiceAdminToken))
|
|
||||||
|
|
||||||
if c.userAgent != "" {
|
|
||||||
req.Header.Set("User-Agent", c.userAgent)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -734,7 +682,8 @@ func (c *Client) GetRunner(ctx context.Context, runnerId int64) (*RunnerReferenc
|
||||||
}
|
}
|
||||||
|
|
||||||
var runnerReference *RunnerReference
|
var runnerReference *RunnerReference
|
||||||
if err := unmarshalBody(resp, &runnerReference); err != nil {
|
err = json.NewDecoder(resp.Body).Decode(&runnerReference)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -742,24 +691,13 @@ func (c *Client) GetRunner(ctx context.Context, runnerId int64) (*RunnerReferenc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetRunnerByName(ctx context.Context, runnerName string) (*RunnerReference, error) {
|
func (c *Client) GetRunnerByName(ctx context.Context, runnerName string) (*RunnerReference, error) {
|
||||||
url := fmt.Sprintf("%v/%v?agentName=%v&%v", *c.ActionsServiceURL, runnerEndpoint, runnerName, apiVersionQueryParam)
|
path := fmt.Sprintf("/%s?agentName=%s", runnerEndpoint, runnerName)
|
||||||
|
|
||||||
if err := c.refreshTokenIfNeeded(ctx); err != nil {
|
req, err := c.NewActionsServiceRequest(ctx, http.MethodGet, path, nil)
|
||||||
return nil, fmt.Errorf("failed to refresh admin token if needed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *c.ActionsServiceAdminToken))
|
|
||||||
|
|
||||||
if c.userAgent != "" {
|
|
||||||
req.Header.Set("User-Agent", c.userAgent)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -770,7 +708,7 @@ func (c *Client) GetRunnerByName(ctx context.Context, runnerName string) (*Runne
|
||||||
}
|
}
|
||||||
|
|
||||||
var runnerList *RunnerReferenceList
|
var runnerList *RunnerReferenceList
|
||||||
err = unmarshalBody(resp, &runnerList)
|
err = json.NewDecoder(resp.Body).Decode(&runnerList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -787,24 +725,13 @@ func (c *Client) GetRunnerByName(ctx context.Context, runnerName string) (*Runne
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) RemoveRunner(ctx context.Context, runnerId int64) error {
|
func (c *Client) RemoveRunner(ctx context.Context, runnerId int64) error {
|
||||||
url := fmt.Sprintf("%v/%v/%v?%v", *c.ActionsServiceURL, runnerEndpoint, runnerId, apiVersionQueryParam)
|
path := fmt.Sprintf("/%s/%d", runnerEndpoint, runnerId)
|
||||||
|
|
||||||
if err := c.refreshTokenIfNeeded(ctx); err != nil {
|
req, err := c.NewActionsServiceRequest(ctx, http.MethodDelete, path, nil)
|
||||||
return fmt.Errorf("failed to refresh admin token if needed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *c.ActionsServiceAdminToken))
|
|
||||||
|
|
||||||
if c.userAgent != "" {
|
|
||||||
req.Header.Set("User-Agent", c.userAgent)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -823,25 +750,25 @@ type registrationToken struct {
|
||||||
ExpiresAt *time.Time `json:"expires_at,omitempty"`
|
ExpiresAt *time.Time `json:"expires_at,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) getRunnerRegistrationToken(ctx context.Context, githubConfigUrl string, creds ActionsAuth) (*registrationToken, error) {
|
func (c *Client) getRunnerRegistrationToken(ctx context.Context) (*registrationToken, error) {
|
||||||
registrationTokenURL, err := createRegistrationTokenURL(githubConfigUrl)
|
path, err := createRegistrationTokenPath(c.config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, registrationTokenURL, &buf)
|
req, err := c.NewGitHubAPIRequest(ctx, http.MethodPost, path, &buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
bearerToken := ""
|
bearerToken := ""
|
||||||
|
|
||||||
if creds.Token != "" {
|
if c.creds.Token != "" {
|
||||||
encodedToken := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("github:%v", creds.Token)))
|
encodedToken := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("github:%v", c.creds.Token)))
|
||||||
bearerToken = fmt.Sprintf("Basic %v", encodedToken)
|
bearerToken = fmt.Sprintf("Basic %v", encodedToken)
|
||||||
} else {
|
} else {
|
||||||
accessToken, err := c.fetchAccessToken(ctx, githubConfigUrl, creds.AppCreds)
|
accessToken, err := c.fetchAccessToken(ctx, c.config.ConfigURL.String(), c.creds.AppCreds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -851,9 +778,8 @@ func (c *Client) getRunnerRegistrationToken(ctx context.Context, githubConfigUrl
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/vnd.github.v3+json")
|
req.Header.Set("Content-Type", "application/vnd.github.v3+json")
|
||||||
req.Header.Set("Authorization", bearerToken)
|
req.Header.Set("Authorization", bearerToken)
|
||||||
req.Header.Set("User-Agent", c.userAgent)
|
|
||||||
|
|
||||||
c.logger.Info("getting runner registration token", "registrationTokenURL", registrationTokenURL)
|
c.logger.Info("getting runner registration token", "registrationTokenURL", req.URL.String())
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -869,8 +795,8 @@ func (c *Client) getRunnerRegistrationToken(ctx context.Context, githubConfigUrl
|
||||||
return nil, fmt.Errorf("unexpected response from Actions service during registration token call: %v - %v", resp.StatusCode, string(body))
|
return nil, fmt.Errorf("unexpected response from Actions service during registration token call: %v - %v", resp.StatusCode, string(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
registrationToken := ®istrationToken{}
|
var registrationToken *registrationToken
|
||||||
if err := json.NewDecoder(resp.Body).Decode(registrationToken); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(®istrationToken); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -889,21 +815,16 @@ func (c *Client) fetchAccessToken(ctx context.Context, gitHubConfigURL string, c
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := githubAPIURL(gitHubConfigURL, fmt.Sprintf("/app/installations/%v/access_tokens", creds.AppInstallationID))
|
path := fmt.Sprintf("/app/installations/%v/access_tokens", creds.AppInstallationID)
|
||||||
if err != nil {
|
req, err := c.NewGitHubAPIRequest(ctx, http.MethodPost, path, nil)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, u, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/vnd.github+json")
|
req.Header.Set("Content-Type", "application/vnd.github+json")
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessTokenJWT))
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessTokenJWT))
|
||||||
req.Header.Add("User-Agent", c.userAgent)
|
|
||||||
|
|
||||||
c.logger.Info("getting access token for GitHub App auth", "accessTokenURL", u)
|
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 {
|
if err != nil {
|
||||||
|
|
@ -912,8 +833,8 @@ func (c *Client) fetchAccessToken(ctx context.Context, gitHubConfigURL string, c
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
// Format: https://docs.github.com/en/rest/apps/apps#create-an-installation-access-token-for-an-app
|
// Format: https://docs.github.com/en/rest/apps/apps#create-an-installation-access-token-for-an-app
|
||||||
accessToken := &accessToken{}
|
var accessToken *accessToken
|
||||||
err = json.NewDecoder(resp.Body).Decode(accessToken)
|
err = json.NewDecoder(resp.Body).Decode(&accessToken)
|
||||||
return accessToken, err
|
return accessToken, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -922,27 +843,14 @@ type ActionsServiceAdminConnection struct {
|
||||||
AdminToken *string `json:"token,omitempty"`
|
AdminToken *string `json:"token,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) getActionsServiceAdminConnection(ctx context.Context, rt *registrationToken, githubConfigUrl string) (*ActionsServiceAdminConnection, error) {
|
func (c *Client) getActionsServiceAdminConnection(ctx context.Context, rt *registrationToken) (*ActionsServiceAdminConnection, error) {
|
||||||
parsedGitHubConfigURL, err := url.Parse(githubConfigUrl)
|
path := "/actions/runner-registration"
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if isHostedServer(*parsedGitHubConfigURL) {
|
|
||||||
parsedGitHubConfigURL.Host = fmt.Sprintf("api.%v", parsedGitHubConfigURL.Host)
|
|
||||||
}
|
|
||||||
|
|
||||||
ru := fmt.Sprintf("%v://%v/actions/runner-registration", parsedGitHubConfigURL.Scheme, parsedGitHubConfigURL.Host)
|
|
||||||
registrationURL, err := url.Parse(ru)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
body := struct {
|
body := struct {
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
RunnerEvent string `json:"runner_event"`
|
RunnerEvent string `json:"runner_event"`
|
||||||
}{
|
}{
|
||||||
Url: githubConfigUrl,
|
Url: c.config.ConfigURL.String(),
|
||||||
RunnerEvent: "register",
|
RunnerEvent: "register",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -954,16 +862,15 @@ func (c *Client) getActionsServiceAdminConnection(ctx context.Context, rt *regis
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, registrationURL.String(), buf)
|
req, err := c.NewGitHubAPIRequest(ctx, http.MethodPost, path, buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("RemoteAuth %s", *rt.Token))
|
req.Header.Set("Authorization", fmt.Sprintf("RemoteAuth %s", *rt.Token))
|
||||||
req.Header.Set("User-Agent", c.userAgent)
|
|
||||||
|
|
||||||
c.logger.Info("getting Actions tenant URL and JWT", "registrationURL", registrationURL.String())
|
c.logger.Info("getting Actions tenant URL and JWT", "registrationURL", req.URL.String())
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -971,65 +878,30 @@ func (c *Client) getActionsServiceAdminConnection(ctx context.Context, rt *regis
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
actionsServiceAdminConnection := &ActionsServiceAdminConnection{}
|
var actionsServiceAdminConnection *ActionsServiceAdminConnection
|
||||||
if err := json.NewDecoder(resp.Body).Decode(actionsServiceAdminConnection); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&actionsServiceAdminConnection); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return actionsServiceAdminConnection, nil
|
return actionsServiceAdminConnection, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isHostedServer(gitHubURL url.URL) bool {
|
func createRegistrationTokenPath(config *GitHubConfig) (string, error) {
|
||||||
return gitHubURL.Host == "github.com" ||
|
switch config.Scope {
|
||||||
gitHubURL.Host == "www.github.com" ||
|
case GitHubScopeOrganization:
|
||||||
gitHubURL.Host == "github.localhost"
|
path := fmt.Sprintf("/orgs/%s/actions/runners/registration-token", config.Organization)
|
||||||
}
|
return path, nil
|
||||||
|
|
||||||
func createRegistrationTokenURL(githubConfigUrl string) (string, error) {
|
case GitHubScopeEnterprise:
|
||||||
parsedGitHubConfigURL, err := url.Parse(githubConfigUrl)
|
path := fmt.Sprintf("/enterprises/%s/actions/runners/registration-token", config.Enterprise)
|
||||||
if err != nil {
|
return path, nil
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for empty path before split, because strings.Split will return a slice of length 1
|
case GitHubScopeRepository:
|
||||||
// when the split delimiter is not present.
|
path := fmt.Sprintf("/repos/%s/%s/actions/runners/registration-token", config.Organization, config.Repository)
|
||||||
trimmedPath := strings.TrimLeft(parsedGitHubConfigURL.Path, "/")
|
return path, nil
|
||||||
if len(trimmedPath) == 0 {
|
|
||||||
return "", fmt.Errorf("%q should point to an enterprise, org, or repository", parsedGitHubConfigURL.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
pathParts := strings.Split(path.Clean(strings.TrimLeft(parsedGitHubConfigURL.Path, "/")), "/")
|
|
||||||
|
|
||||||
switch len(pathParts) {
|
|
||||||
case 1: // Organization
|
|
||||||
registrationTokenURL := fmt.Sprintf(
|
|
||||||
"%v://%v/api/v3/orgs/%v/actions/runners/registration-token",
|
|
||||||
parsedGitHubConfigURL.Scheme, parsedGitHubConfigURL.Host, pathParts[0])
|
|
||||||
|
|
||||||
if isHostedServer(*parsedGitHubConfigURL) {
|
|
||||||
registrationTokenURL = fmt.Sprintf(
|
|
||||||
"%v://api.%v/orgs/%v/actions/runners/registration-token",
|
|
||||||
parsedGitHubConfigURL.Scheme, parsedGitHubConfigURL.Host, pathParts[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
return registrationTokenURL, nil
|
|
||||||
case 2: // Repository or enterprise
|
|
||||||
repoScope := "repos/"
|
|
||||||
if strings.ToLower(pathParts[0]) == "enterprises" {
|
|
||||||
repoScope = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
registrationTokenURL := fmt.Sprintf("%v://%v/api/v3/%v%v/%v/actions/runners/registration-token",
|
|
||||||
parsedGitHubConfigURL.Scheme, parsedGitHubConfigURL.Host, repoScope, pathParts[0], pathParts[1])
|
|
||||||
|
|
||||||
if isHostedServer(*parsedGitHubConfigURL) {
|
|
||||||
registrationTokenURL = fmt.Sprintf("%v://api.%v/%v%v/%v/actions/runners/registration-token",
|
|
||||||
parsedGitHubConfigURL.Scheme, parsedGitHubConfigURL.Host, repoScope, pathParts[0], pathParts[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
return registrationTokenURL, nil
|
|
||||||
default:
|
default:
|
||||||
return "", fmt.Errorf("%q should point to an enterprise, org, or repository", parsedGitHubConfigURL.String())
|
return "", fmt.Errorf("unknown scope for config url: %s", config.ConfigURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1057,68 +929,50 @@ func createJWTForGitHubApp(appAuth *GitHubAppAuth) (string, error) {
|
||||||
return token.SignedString(privateKey)
|
return token.SignedString(privateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalBody(response *http.Response, v interface{}) (err error) {
|
|
||||||
if response != nil && response.Body != nil {
|
|
||||||
var err error
|
|
||||||
defer func() {
|
|
||||||
if closeError := response.Body.Close(); closeError != nil {
|
|
||||||
err = closeError
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
body, err := io.ReadAll(response.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
body = trimByteOrderMark(body)
|
|
||||||
return json.Unmarshal(body, &v)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns slice of body without utf-8 byte order mark.
|
// Returns slice of body without utf-8 byte order mark.
|
||||||
// If BOM does not exist body is returned unchanged.
|
// If BOM does not exist body is returned unchanged.
|
||||||
func trimByteOrderMark(body []byte) []byte {
|
func trimByteOrderMark(body []byte) []byte {
|
||||||
return bytes.TrimPrefix(body, []byte("\xef\xbb\xbf"))
|
return bytes.TrimPrefix(body, []byte("\xef\xbb\xbf"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func actionsServiceAdminTokenExpiresAt(jwtToken string) (*time.Time, error) {
|
func actionsServiceAdminTokenExpiresAt(jwtToken string) (time.Time, error) {
|
||||||
type JwtClaims struct {
|
type JwtClaims struct {
|
||||||
jwt.RegisteredClaims
|
jwt.RegisteredClaims
|
||||||
}
|
}
|
||||||
token, _, err := jwt.NewParser().ParseUnverified(jwtToken, &JwtClaims{})
|
token, _, err := jwt.NewParser().ParseUnverified(jwtToken, &JwtClaims{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse jwt token: %w", err)
|
return time.Time{}, fmt.Errorf("failed to parse jwt token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if claims, ok := token.Claims.(*JwtClaims); ok {
|
if claims, ok := token.Claims.(*JwtClaims); ok {
|
||||||
return &claims.ExpiresAt.Time, nil
|
return claims.ExpiresAt.Time, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("failed to parse token claims to get expire at")
|
return time.Time{}, fmt.Errorf("failed to parse token claims to get expire at")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) refreshTokenIfNeeded(ctx context.Context) error {
|
func (c *Client) updateTokenIfNeeded(ctx context.Context) error {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
aboutToExpire := time.Now().Add(60 * time.Second).After(*c.ActionsServiceAdminTokenExpiresAt)
|
aboutToExpire := time.Now().Add(60 * time.Second).After(c.ActionsServiceAdminTokenExpiresAt)
|
||||||
if !aboutToExpire {
|
if !aboutToExpire && !c.ActionsServiceAdminTokenExpiresAt.IsZero() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
c.logger.Info("Admin token is about to expire, refreshing it", "githubConfigUrl", c.githubConfigURL)
|
c.logger.Info("refreshing token", "githubConfigUrl", c.config.ConfigURL.String())
|
||||||
rt, err := c.getRunnerRegistrationToken(ctx, c.githubConfigURL, *c.creds)
|
rt, err := c.getRunnerRegistrationToken(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get runner registration token on fresh: %w", err)
|
return fmt.Errorf("failed to get runner registration token on refresh: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
adminConnInfo, err := c.getActionsServiceAdminConnection(ctx, rt, c.githubConfigURL)
|
adminConnInfo, err := c.getActionsServiceAdminConnection(ctx, rt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get actions service admin connection on fresh: %w", err)
|
return fmt.Errorf("failed to get actions service admin connection on refresh: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ActionsServiceURL = adminConnInfo.ActionsServiceUrl
|
c.ActionsServiceURL = *adminConnInfo.ActionsServiceUrl
|
||||||
c.ActionsServiceAdminToken = adminConnInfo.AdminToken
|
c.ActionsServiceAdminToken = *adminConnInfo.AdminToken
|
||||||
c.ActionsServiceAdminTokenExpiresAt, err = actionsServiceAdminTokenExpiresAt(*adminConnInfo.AdminToken)
|
c.ActionsServiceAdminTokenExpiresAt, err = actionsServiceAdminTokenExpiresAt(*adminConnInfo.AdminToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get admin token expire at on refresh: %w", err)
|
return fmt.Errorf("failed to get admin token expire at on refresh: %w", err)
|
||||||
|
|
@ -1126,32 +980,3 @@ func (c *Client) refreshTokenIfNeeded(ctx context.Context) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func githubAPIURL(configURL, path string) (string, error) {
|
|
||||||
u, err := url.Parse(configURL)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
result := &url.URL{
|
|
||||||
Scheme: u.Scheme,
|
|
||||||
}
|
|
||||||
|
|
||||||
switch u.Host {
|
|
||||||
// Hosted
|
|
||||||
case "github.com", "github.localhost":
|
|
||||||
result.Host = fmt.Sprintf("api.%s", u.Host)
|
|
||||||
// re-routing www.github.com to api.github.com
|
|
||||||
case "www.github.com":
|
|
||||||
result.Host = "api.github.com"
|
|
||||||
|
|
||||||
// Enterprise
|
|
||||||
default:
|
|
||||||
result.Host = u.Host
|
|
||||||
result.Path = "/api/v3"
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Path += path
|
|
||||||
|
|
||||||
return result.String(), nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ func TestGenerateJitRunnerConfig(t *testing.T) {
|
||||||
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
w.Write(response)
|
w.Write(response)
|
||||||
}))
|
}))
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := client.GenerateJitRunnerConfig(ctx, runnerSettings, 1)
|
got, err := client.GenerateJitRunnerConfig(ctx, runnerSettings, 1)
|
||||||
|
|
@ -47,7 +47,6 @@ func TestGenerateJitRunnerConfig(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(
|
client, err := actions.NewClient(
|
||||||
ctx,
|
|
||||||
server.configURLForOrg("my-org"),
|
server.configURLForOrg("my-org"),
|
||||||
auth,
|
auth,
|
||||||
actions.WithRetryMax(1),
|
actions.WithRetryMax(1),
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package actions_test
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -27,11 +28,19 @@ func TestAcquireJobs(t *testing.T) {
|
||||||
}
|
}
|
||||||
requestIDs := want
|
requestIDs := want
|
||||||
|
|
||||||
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if strings.HasSuffix(r.URL.Path, "/acquirablejobs") {
|
||||||
|
w.Write([]byte(`{"count": 1}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
w.Write(response)
|
w.Write(response)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = client.GetAcquirableJobs(ctx, 1)
|
||||||
require.NoError(t, err)
|
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)
|
||||||
|
|
@ -50,13 +59,17 @@ func TestAcquireJobs(t *testing.T) {
|
||||||
actualRetry := 0
|
actualRetry := 0
|
||||||
expectedRetry := retryMax + 1
|
expectedRetry := retryMax + 1
|
||||||
|
|
||||||
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if strings.HasSuffix(r.URL.Path, "/acquirablejobs") {
|
||||||
|
w.Write([]byte(`{"count": 1}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusServiceUnavailable)
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
actualRetry++
|
actualRetry++
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(
|
client, err := actions.NewClient(
|
||||||
ctx,
|
|
||||||
server.configURLForOrg("my-org"),
|
server.configURLForOrg("my-org"),
|
||||||
auth,
|
auth,
|
||||||
actions.WithRetryMax(retryMax),
|
actions.WithRetryMax(retryMax),
|
||||||
|
|
@ -64,6 +77,9 @@ func TestAcquireJobs(t *testing.T) {
|
||||||
)
|
)
|
||||||
require.NoError(t, err)
|
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.NotNil(t, err)
|
||||||
assert.Equalf(t, actualRetry, expectedRetry, "A retry was expected after the first request but got: %v", actualRetry)
|
assert.Equalf(t, actualRetry, expectedRetry, "A retry was expected after the first request but got: %v", actualRetry)
|
||||||
|
|
@ -71,7 +87,6 @@ func TestAcquireJobs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetAcquirableJobs(t *testing.T) {
|
func TestGetAcquirableJobs(t *testing.T) {
|
||||||
ctx := context.Background()
|
|
||||||
auth := &actions.ActionsAuth{
|
auth := &actions.ActionsAuth{
|
||||||
Token: "token",
|
Token: "token",
|
||||||
}
|
}
|
||||||
|
|
@ -86,7 +101,7 @@ func TestGetAcquirableJobs(t *testing.T) {
|
||||||
w.Write(response)
|
w.Write(response)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := client.GetAcquirableJobs(context.Background(), runnerScaleSet.Id)
|
got, err := client.GetAcquirableJobs(context.Background(), runnerScaleSet.Id)
|
||||||
|
|
@ -108,7 +123,6 @@ func TestGetAcquirableJobs(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(
|
client, err := actions.NewClient(
|
||||||
context.Background(),
|
|
||||||
server.configURLForOrg("my-org"),
|
server.configURLForOrg("my-org"),
|
||||||
auth,
|
auth,
|
||||||
actions.WithRetryMax(retryMax),
|
actions.WithRetryMax(retryMax),
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ func TestGetMessage(t *testing.T) {
|
||||||
w.Write(response)
|
w.Write(response)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, s.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(s.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := client.GetMessage(ctx, s.URL, token, 0)
|
got, err := client.GetMessage(ctx, s.URL, token, 0)
|
||||||
|
|
@ -40,6 +40,23 @@ func TestGetMessage(t *testing.T) {
|
||||||
assert.Equal(t, want, got)
|
assert.Equal(t, want, got)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("GetMessage sets the last message id if not 0", func(t *testing.T) {
|
||||||
|
want := runnerScaleSetMessage
|
||||||
|
response := []byte(`{"messageId":1,"messageType":"rssType"}`)
|
||||||
|
s := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
q := r.URL.Query()
|
||||||
|
assert.Equal(t, "1", q.Get("lastMessageId"))
|
||||||
|
w.Write(response)
|
||||||
|
}))
|
||||||
|
|
||||||
|
client, err := actions.NewClient(s.configURLForOrg("my-org"), auth)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
got, err := client.GetMessage(ctx, s.URL, token, 1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, want, got)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Default retries on server error", func(t *testing.T) {
|
t.Run("Default retries on server error", func(t *testing.T) {
|
||||||
retryMax := 1
|
retryMax := 1
|
||||||
|
|
||||||
|
|
@ -52,7 +69,6 @@ func TestGetMessage(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(
|
client, err := actions.NewClient(
|
||||||
ctx,
|
|
||||||
server.configURLForOrg("my-org"),
|
server.configURLForOrg("my-org"),
|
||||||
auth,
|
auth,
|
||||||
actions.WithRetryMax(retryMax),
|
actions.WithRetryMax(retryMax),
|
||||||
|
|
@ -70,7 +86,7 @@ func TestGetMessage(t *testing.T) {
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.GetMessage(ctx, server.URL, token, 0)
|
_, err = client.GetMessage(ctx, server.URL, token, 0)
|
||||||
|
|
@ -78,8 +94,7 @@ func TestGetMessage(t *testing.T) {
|
||||||
|
|
||||||
var expectedErr *actions.MessageQueueTokenExpiredError
|
var expectedErr *actions.MessageQueueTokenExpiredError
|
||||||
require.True(t, errors.As(err, &expectedErr))
|
require.True(t, errors.As(err, &expectedErr))
|
||||||
},
|
})
|
||||||
)
|
|
||||||
|
|
||||||
t.Run("Status code not found", func(t *testing.T) {
|
t.Run("Status code not found", func(t *testing.T) {
|
||||||
want := actions.ActionsError{
|
want := actions.ActionsError{
|
||||||
|
|
@ -90,7 +105,7 @@ func TestGetMessage(t *testing.T) {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.GetMessage(ctx, server.URL, token, 0)
|
_, err = client.GetMessage(ctx, server.URL, token, 0)
|
||||||
|
|
@ -104,7 +119,7 @@ func TestGetMessage(t *testing.T) {
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.GetMessage(ctx, server.URL, token, 0)
|
_, err = client.GetMessage(ctx, server.URL, token, 0)
|
||||||
|
|
@ -129,7 +144,7 @@ func TestDeleteMessage(t *testing.T) {
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageId)
|
err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageId)
|
||||||
|
|
@ -141,7 +156,7 @@ func TestDeleteMessage(t *testing.T) {
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = client.DeleteMessage(ctx, server.URL, token, 0)
|
err = client.DeleteMessage(ctx, server.URL, token, 0)
|
||||||
|
|
@ -156,7 +171,7 @@ func TestDeleteMessage(t *testing.T) {
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageId)
|
err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageId)
|
||||||
|
|
@ -175,7 +190,6 @@ func TestDeleteMessage(t *testing.T) {
|
||||||
|
|
||||||
retryMax := 1
|
retryMax := 1
|
||||||
client, err := actions.NewClient(
|
client, err := actions.NewClient(
|
||||||
ctx,
|
|
||||||
server.configURLForOrg("my-org"),
|
server.configURLForOrg("my-org"),
|
||||||
auth,
|
auth,
|
||||||
actions.WithRetryMax(retryMax),
|
actions.WithRetryMax(retryMax),
|
||||||
|
|
@ -197,7 +211,7 @@ func TestDeleteMessage(t *testing.T) {
|
||||||
w.Write(rsl)
|
w.Write(rsl)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageId+1)
|
err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageId+1)
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ func TestCreateMessageSession(t *testing.T) {
|
||||||
w.Write(resp)
|
w.Write(resp)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := client.CreateMessageSession(ctx, runnerScaleSet.Id, owner)
|
got, err := client.CreateMessageSession(ctx, runnerScaleSet.Id, owner)
|
||||||
|
|
@ -81,7 +81,7 @@ func TestCreateMessageSession(t *testing.T) {
|
||||||
w.Write(resp)
|
w.Write(resp)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.CreateMessageSession(ctx, runnerScaleSet.Id, owner)
|
_, err = client.CreateMessageSession(ctx, runnerScaleSet.Id, owner)
|
||||||
|
|
@ -120,7 +120,6 @@ func TestCreateMessageSession(t *testing.T) {
|
||||||
wantRetries := retryMax + 1
|
wantRetries := retryMax + 1
|
||||||
|
|
||||||
client, err := actions.NewClient(
|
client, err := actions.NewClient(
|
||||||
ctx,
|
|
||||||
server.configURLForOrg("my-org"),
|
server.configURLForOrg("my-org"),
|
||||||
auth,
|
auth,
|
||||||
actions.WithRetryMax(retryMax),
|
actions.WithRetryMax(retryMax),
|
||||||
|
|
@ -160,7 +159,6 @@ func TestDeleteMessageSession(t *testing.T) {
|
||||||
wantRetries := retryMax + 1
|
wantRetries := retryMax + 1
|
||||||
|
|
||||||
client, err := actions.NewClient(
|
client, err := actions.NewClient(
|
||||||
ctx,
|
|
||||||
server.configURLForOrg("my-org"),
|
server.configURLForOrg("my-org"),
|
||||||
auth,
|
auth,
|
||||||
actions.WithRetryMax(retryMax),
|
actions.WithRetryMax(retryMax),
|
||||||
|
|
@ -177,7 +175,6 @@ func TestDeleteMessageSession(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRefreshMessageSession(t *testing.T) {
|
func TestRefreshMessageSession(t *testing.T) {
|
||||||
ctx := context.Background()
|
|
||||||
auth := &actions.ActionsAuth{
|
auth := &actions.ActionsAuth{
|
||||||
Token: "token",
|
Token: "token",
|
||||||
}
|
}
|
||||||
|
|
@ -202,7 +199,6 @@ func TestRefreshMessageSession(t *testing.T) {
|
||||||
wantRetries := retryMax + 1
|
wantRetries := retryMax + 1
|
||||||
|
|
||||||
client, err := actions.NewClient(
|
client, err := actions.NewClient(
|
||||||
ctx,
|
|
||||||
server.configURLForOrg("my-org"),
|
server.configURLForOrg("my-org"),
|
||||||
auth,
|
auth,
|
||||||
actions.WithRetryMax(retryMax),
|
actions.WithRetryMax(retryMax),
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ func TestGetRunnerScaleSet(t *testing.T) {
|
||||||
w.Write(runnerScaleSetsResp)
|
w.Write(runnerScaleSetsResp)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := client.GetRunnerScaleSet(ctx, scaleSetName)
|
got, err := client.GetRunnerScaleSet(ctx, scaleSetName)
|
||||||
|
|
@ -47,15 +47,16 @@ func TestGetRunnerScaleSet(t *testing.T) {
|
||||||
url = *r.URL
|
url = *r.URL
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.GetRunnerScaleSet(ctx, scaleSetName)
|
_, err = client.GetRunnerScaleSet(ctx, scaleSetName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
u := url.String()
|
expectedPath := "/tenant/123/_apis/runtime/runnerscalesets"
|
||||||
expectedUrl := fmt.Sprintf("/_apis/runtime/runnerscalesets?name=%s&api-version=6.0-preview", scaleSetName)
|
assert.Equal(t, expectedPath, url.Path)
|
||||||
assert.Equal(t, expectedUrl, u)
|
assert.Equal(t, scaleSetName, url.Query().Get("name"))
|
||||||
|
assert.Equal(t, "6.0-preview", url.Query().Get("api-version"))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Status code not found", func(t *testing.T) {
|
t.Run("Status code not found", func(t *testing.T) {
|
||||||
|
|
@ -63,7 +64,7 @@ func TestGetRunnerScaleSet(t *testing.T) {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.GetRunnerScaleSet(ctx, scaleSetName)
|
_, err = client.GetRunnerScaleSet(ctx, scaleSetName)
|
||||||
|
|
@ -76,7 +77,7 @@ func TestGetRunnerScaleSet(t *testing.T) {
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.GetRunnerScaleSet(ctx, scaleSetName)
|
_, err = client.GetRunnerScaleSet(ctx, scaleSetName)
|
||||||
|
|
@ -94,7 +95,6 @@ func TestGetRunnerScaleSet(t *testing.T) {
|
||||||
retryWaitMax := 1 * time.Microsecond
|
retryWaitMax := 1 * time.Microsecond
|
||||||
|
|
||||||
client, err := actions.NewClient(
|
client, err := actions.NewClient(
|
||||||
ctx,
|
|
||||||
server.configURLForOrg("my-org"),
|
server.configURLForOrg("my-org"),
|
||||||
auth,
|
auth,
|
||||||
actions.WithRetryMax(retryMax),
|
actions.WithRetryMax(retryMax),
|
||||||
|
|
@ -115,7 +115,7 @@ func TestGetRunnerScaleSet(t *testing.T) {
|
||||||
w.Write(runnerScaleSetsResp)
|
w.Write(runnerScaleSetsResp)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := client.GetRunnerScaleSet(ctx, scaleSetName)
|
got, err := client.GetRunnerScaleSet(ctx, scaleSetName)
|
||||||
|
|
@ -130,7 +130,7 @@ func TestGetRunnerScaleSet(t *testing.T) {
|
||||||
w.Write(runnerScaleSetsResp)
|
w.Write(runnerScaleSetsResp)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.GetRunnerScaleSet(ctx, scaleSetName)
|
_, err = client.GetRunnerScaleSet(ctx, scaleSetName)
|
||||||
|
|
@ -156,7 +156,7 @@ func TestGetRunnerScaleSetById(t *testing.T) {
|
||||||
w.Write(rsl)
|
w.Write(rsl)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, sservere.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(sservere.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := client.GetRunnerScaleSetById(ctx, runnerScaleSet.Id)
|
got, err := client.GetRunnerScaleSetById(ctx, runnerScaleSet.Id)
|
||||||
|
|
@ -174,15 +174,15 @@ func TestGetRunnerScaleSetById(t *testing.T) {
|
||||||
url = *r.URL
|
url = *r.URL
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.GetRunnerScaleSetById(ctx, runnerScaleSet.Id)
|
_, err = client.GetRunnerScaleSetById(ctx, runnerScaleSet.Id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
u := url.String()
|
expectedPath := fmt.Sprintf("/tenant/123/_apis/runtime/runnerscalesets/%d", runnerScaleSet.Id)
|
||||||
expectedUrl := fmt.Sprintf("/_apis/runtime/runnerscalesets/%d?api-version=6.0-preview", runnerScaleSet.Id)
|
assert.Equal(t, expectedPath, url.Path)
|
||||||
assert.Equal(t, expectedUrl, u)
|
assert.Equal(t, "6.0-preview", url.Query().Get("api-version"))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Status code not found", func(t *testing.T) {
|
t.Run("Status code not found", func(t *testing.T) {
|
||||||
|
|
@ -190,7 +190,7 @@ func TestGetRunnerScaleSetById(t *testing.T) {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.GetRunnerScaleSetById(ctx, runnerScaleSet.Id)
|
_, err = client.GetRunnerScaleSetById(ctx, runnerScaleSet.Id)
|
||||||
|
|
@ -203,7 +203,7 @@ func TestGetRunnerScaleSetById(t *testing.T) {
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.GetRunnerScaleSetById(ctx, runnerScaleSet.Id)
|
_, err = client.GetRunnerScaleSetById(ctx, runnerScaleSet.Id)
|
||||||
|
|
@ -220,7 +220,6 @@ func TestGetRunnerScaleSetById(t *testing.T) {
|
||||||
retryMax := 1
|
retryMax := 1
|
||||||
retryWaitMax := 1 * time.Microsecond
|
retryWaitMax := 1 * time.Microsecond
|
||||||
client, err := actions.NewClient(
|
client, err := actions.NewClient(
|
||||||
ctx,
|
|
||||||
server.configURLForOrg("my-org"),
|
server.configURLForOrg("my-org"),
|
||||||
auth,
|
auth,
|
||||||
actions.WithRetryMax(retryMax),
|
actions.WithRetryMax(retryMax),
|
||||||
|
|
@ -242,7 +241,7 @@ func TestGetRunnerScaleSetById(t *testing.T) {
|
||||||
w.Write(rsl)
|
w.Write(rsl)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := client.GetRunnerScaleSetById(ctx, runnerScaleSet.Id)
|
got, err := client.GetRunnerScaleSetById(ctx, runnerScaleSet.Id)
|
||||||
|
|
@ -268,7 +267,7 @@ func TestCreateRunnerScaleSet(t *testing.T) {
|
||||||
w.Write(rsl)
|
w.Write(rsl)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := client.CreateRunnerScaleSet(ctx, &runnerScaleSet)
|
got, err := client.CreateRunnerScaleSet(ctx, &runnerScaleSet)
|
||||||
|
|
@ -285,15 +284,15 @@ func TestCreateRunnerScaleSet(t *testing.T) {
|
||||||
url = *r.URL
|
url = *r.URL
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.CreateRunnerScaleSet(ctx, &runnerScaleSet)
|
_, err = client.CreateRunnerScaleSet(ctx, &runnerScaleSet)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
u := url.String()
|
expectedPath := "/tenant/123/_apis/runtime/runnerscalesets"
|
||||||
expectedUrl := "/_apis/runtime/runnerscalesets?api-version=6.0-preview"
|
assert.Equal(t, expectedPath, url.Path)
|
||||||
assert.Equal(t, expectedUrl, u)
|
assert.Equal(t, "6.0-preview", url.Query().Get("api-version"))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Error when Content-Type is text/plain", func(t *testing.T) {
|
t.Run("Error when Content-Type is text/plain", func(t *testing.T) {
|
||||||
|
|
@ -302,7 +301,7 @@ func TestCreateRunnerScaleSet(t *testing.T) {
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.CreateRunnerScaleSet(ctx, &runnerScaleSet)
|
_, err = client.CreateRunnerScaleSet(ctx, &runnerScaleSet)
|
||||||
|
|
@ -322,7 +321,6 @@ func TestCreateRunnerScaleSet(t *testing.T) {
|
||||||
retryWaitMax := 1 * time.Microsecond
|
retryWaitMax := 1 * time.Microsecond
|
||||||
|
|
||||||
client, err := actions.NewClient(
|
client, err := actions.NewClient(
|
||||||
ctx,
|
|
||||||
server.configURLForOrg("my-org"),
|
server.configURLForOrg("my-org"),
|
||||||
auth,
|
auth,
|
||||||
actions.WithRetryMax(retryMax),
|
actions.WithRetryMax(retryMax),
|
||||||
|
|
@ -354,7 +352,7 @@ func TestUpdateRunnerScaleSet(t *testing.T) {
|
||||||
w.Write(rsl)
|
w.Write(rsl)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := client.UpdateRunnerScaleSet(ctx, 1, &actions.RunnerScaleSet{RunnerGroupId: 1})
|
got, err := client.UpdateRunnerScaleSet(ctx, 1, &actions.RunnerScaleSet{RunnerGroupId: 1})
|
||||||
|
|
@ -365,24 +363,19 @@ func TestUpdateRunnerScaleSet(t *testing.T) {
|
||||||
t.Run("UpdateRunnerScaleSet calls correct url", func(t *testing.T) {
|
t.Run("UpdateRunnerScaleSet calls correct url", func(t *testing.T) {
|
||||||
rsl, err := json.Marshal(&runnerScaleSet)
|
rsl, err := json.Marshal(&runnerScaleSet)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
url := url.URL{}
|
|
||||||
method := ""
|
|
||||||
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
expectedPath := "/tenant/123/_apis/runtime/runnerscalesets/1"
|
||||||
|
assert.Equal(t, expectedPath, r.URL.Path)
|
||||||
|
assert.Equal(t, http.MethodPatch, r.Method)
|
||||||
|
assert.Equal(t, "6.0-preview", r.URL.Query().Get("api-version"))
|
||||||
|
|
||||||
w.Write(rsl)
|
w.Write(rsl)
|
||||||
url = *r.URL
|
|
||||||
method = r.Method
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.UpdateRunnerScaleSet(ctx, 1, &runnerScaleSet)
|
_, err = client.UpdateRunnerScaleSet(ctx, 1, &runnerScaleSet)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
u := url.String()
|
|
||||||
expectedUrl := "/_apis/runtime/runnerscalesets/1?api-version=6.0-preview"
|
|
||||||
assert.Equal(t, expectedUrl, u)
|
|
||||||
|
|
||||||
assert.Equal(t, "PATCH", method)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ func TestGetRunner(t *testing.T) {
|
||||||
w.Write(response)
|
w.Write(response)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := client.GetRunner(ctx, runnerID)
|
got, err := client.GetRunner(ctx, runnerID)
|
||||||
|
|
@ -50,7 +50,7 @@ func TestGetRunner(t *testing.T) {
|
||||||
actualRetry++
|
actualRetry++
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth, actions.WithRetryMax(retryMax), actions.WithRetryWaitMax(retryWaitMax))
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth, actions.WithRetryMax(retryMax), actions.WithRetryWaitMax(retryWaitMax))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.GetRunner(ctx, runnerID)
|
_, err = client.GetRunner(ctx, runnerID)
|
||||||
|
|
@ -78,7 +78,7 @@ func TestGetRunnerByName(t *testing.T) {
|
||||||
w.Write(response)
|
w.Write(response)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := client.GetRunnerByName(ctx, runnerName)
|
got, err := client.GetRunnerByName(ctx, runnerName)
|
||||||
|
|
@ -94,7 +94,7 @@ func TestGetRunnerByName(t *testing.T) {
|
||||||
w.Write(response)
|
w.Write(response)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := client.GetRunnerByName(ctx, runnerName)
|
got, err := client.GetRunnerByName(ctx, runnerName)
|
||||||
|
|
@ -116,7 +116,7 @@ func TestGetRunnerByName(t *testing.T) {
|
||||||
actualRetry++
|
actualRetry++
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth, actions.WithRetryMax(retryMax), actions.WithRetryWaitMax(retryWaitMax))
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth, actions.WithRetryMax(retryMax), actions.WithRetryWaitMax(retryWaitMax))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.GetRunnerByName(ctx, runnerName)
|
_, err = client.GetRunnerByName(ctx, runnerName)
|
||||||
|
|
@ -138,7 +138,7 @@ func TestDeleteRunner(t *testing.T) {
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = client.RemoveRunner(ctx, runnerID)
|
err = client.RemoveRunner(ctx, runnerID)
|
||||||
|
|
@ -160,7 +160,6 @@ func TestDeleteRunner(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(
|
client, err := actions.NewClient(
|
||||||
ctx,
|
|
||||||
server.configURLForOrg("my-org"),
|
server.configURLForOrg("my-org"),
|
||||||
auth,
|
auth,
|
||||||
actions.WithRetryMax(retryMax),
|
actions.WithRetryMax(retryMax),
|
||||||
|
|
@ -193,7 +192,7 @@ func TestGetRunnerGroupByName(t *testing.T) {
|
||||||
w.Write(response)
|
w.Write(response)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := client.GetRunnerGroupByName(ctx, runnerGroupName)
|
got, err := client.GetRunnerGroupByName(ctx, runnerGroupName)
|
||||||
|
|
@ -209,7 +208,7 @@ func TestGetRunnerGroupByName(t *testing.T) {
|
||||||
w.Write(response)
|
w.Write(response)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := client.GetRunnerGroupByName(ctx, runnerGroupName)
|
got, err := client.GetRunnerGroupByName(ctx, runnerGroupName)
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ import (
|
||||||
|
|
||||||
func TestServerWithSelfSignedCertificates(t *testing.T) {
|
func TestServerWithSelfSignedCertificates(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// this handler is a very very barebones replica of actions api
|
// this handler is a very very barebones replica of actions api
|
||||||
// used during the creation of a a new client
|
// used during the creation of a a new client
|
||||||
|
var u string
|
||||||
h := func(w http.ResponseWriter, r *http.Request) {
|
h := func(w http.ResponseWriter, r *http.Request) {
|
||||||
// handle get registration token
|
// handle get registration token
|
||||||
if strings.HasSuffix(r.URL.Path, "/runners/registration-token") {
|
if strings.HasSuffix(r.URL.Path, "/runners/registration-token") {
|
||||||
|
|
@ -46,9 +46,12 @@ func TestServerWithSelfSignedCertificates(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
tokenString, err := token.SignedString(privateKey)
|
tokenString, err := token.SignedString(privateKey)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
w.Write([]byte(`{"url":"TODO","token":"` + tokenString + `"}`))
|
w.Write([]byte(`{"url":"` + u + `","token":"` + tokenString + `"}`))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// default happy response for RemoveRunner
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
certPath := filepath.Join("testdata", "server.crt")
|
certPath := filepath.Join("testdata", "server.crt")
|
||||||
|
|
@ -56,13 +59,17 @@ func TestServerWithSelfSignedCertificates(t *testing.T) {
|
||||||
|
|
||||||
t.Run("client without ca certs", func(t *testing.T) {
|
t.Run("client without ca certs", func(t *testing.T) {
|
||||||
server := startNewTLSTestServer(t, certPath, keyPath, http.HandlerFunc(h))
|
server := startNewTLSTestServer(t, certPath, keyPath, http.HandlerFunc(h))
|
||||||
|
u = server.URL
|
||||||
configURL := server.URL + "/my-org"
|
configURL := server.URL + "/my-org"
|
||||||
|
|
||||||
auth := &actions.ActionsAuth{
|
auth := &actions.ActionsAuth{
|
||||||
Token: "token",
|
Token: "token",
|
||||||
}
|
}
|
||||||
client, err := actions.NewClient(ctx, configURL, auth)
|
client, err := actions.NewClient(configURL, auth)
|
||||||
assert.Nil(t, client)
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, client)
|
||||||
|
|
||||||
|
err = client.RemoveRunner(ctx, 1)
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
|
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
|
|
@ -78,6 +85,7 @@ func TestServerWithSelfSignedCertificates(t *testing.T) {
|
||||||
|
|
||||||
t.Run("client with ca certs", func(t *testing.T) {
|
t.Run("client with ca certs", func(t *testing.T) {
|
||||||
server := startNewTLSTestServer(t, certPath, keyPath, http.HandlerFunc(h))
|
server := startNewTLSTestServer(t, certPath, keyPath, http.HandlerFunc(h))
|
||||||
|
u = server.URL
|
||||||
configURL := server.URL + "/my-org"
|
configURL := server.URL + "/my-org"
|
||||||
|
|
||||||
auth := &actions.ActionsAuth{
|
auth := &actions.ActionsAuth{
|
||||||
|
|
@ -90,9 +98,12 @@ func TestServerWithSelfSignedCertificates(t *testing.T) {
|
||||||
pool, err := actions.RootCAsFromConfigMap(map[string][]byte{"cert": cert})
|
pool, err := actions.RootCAsFromConfigMap(map[string][]byte{"cert": cert})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, configURL, auth, actions.WithRootCAs(pool))
|
client, err := actions.NewClient(configURL, auth, actions.WithRootCAs(pool))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.NotNil(t, client)
|
assert.NotNil(t, client)
|
||||||
|
|
||||||
|
err = client.RemoveRunner(ctx, 1)
|
||||||
|
assert.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("client with ca chain certs", func(t *testing.T) {
|
t.Run("client with ca chain certs", func(t *testing.T) {
|
||||||
|
|
@ -102,6 +113,7 @@ func TestServerWithSelfSignedCertificates(t *testing.T) {
|
||||||
filepath.Join("testdata", "leaf.key"),
|
filepath.Join("testdata", "leaf.key"),
|
||||||
http.HandlerFunc(h),
|
http.HandlerFunc(h),
|
||||||
)
|
)
|
||||||
|
u = server.URL
|
||||||
configURL := server.URL + "/my-org"
|
configURL := server.URL + "/my-org"
|
||||||
|
|
||||||
auth := &actions.ActionsAuth{
|
auth := &actions.ActionsAuth{
|
||||||
|
|
@ -114,9 +126,12 @@ func TestServerWithSelfSignedCertificates(t *testing.T) {
|
||||||
pool, err := actions.RootCAsFromConfigMap(map[string][]byte{"cert": cert})
|
pool, err := actions.RootCAsFromConfigMap(map[string][]byte{"cert": cert})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, configURL, auth, actions.WithRootCAs(pool), actions.WithRetryMax(0))
|
client, err := actions.NewClient(configURL, auth, actions.WithRootCAs(pool), actions.WithRetryMax(0))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.NotNil(t, client)
|
require.NotNil(t, client)
|
||||||
|
|
||||||
|
err = client.RemoveRunner(ctx, 1)
|
||||||
|
assert.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("client skipping tls verification", func(t *testing.T) {
|
t.Run("client skipping tls verification", func(t *testing.T) {
|
||||||
|
|
@ -127,7 +142,7 @@ func TestServerWithSelfSignedCertificates(t *testing.T) {
|
||||||
Token: "token",
|
Token: "token",
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := actions.NewClient(ctx, configURL, auth, actions.WithoutTLSVerify())
|
client, err := actions.NewClient(configURL, auth, actions.WithoutTLSVerify())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.NotNil(t, client)
|
assert.NotNil(t, client)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
package actions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrInvalidGitHubConfigURL = fmt.Errorf("invalid config URL, should point to an enterprise, org, or repository")
|
||||||
|
|
||||||
|
type GitHubScope int
|
||||||
|
|
||||||
|
const (
|
||||||
|
GitHubScopeUnknown GitHubScope = iota
|
||||||
|
GitHubScopeEnterprise
|
||||||
|
GitHubScopeOrganization
|
||||||
|
GitHubScopeRepository
|
||||||
|
)
|
||||||
|
|
||||||
|
type GitHubConfig struct {
|
||||||
|
ConfigURL *url.URL
|
||||||
|
Scope GitHubScope
|
||||||
|
|
||||||
|
Enterprise string
|
||||||
|
Organization string
|
||||||
|
Repository string
|
||||||
|
|
||||||
|
IsHosted bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseGitHubConfigFromURL(in string) (*GitHubConfig, error) {
|
||||||
|
u, err := url.Parse(in)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
isHosted := u.Host == "github.com" ||
|
||||||
|
u.Host == "www.github.com" ||
|
||||||
|
u.Host == "github.localhost"
|
||||||
|
|
||||||
|
configURL := &GitHubConfig{
|
||||||
|
ConfigURL: u,
|
||||||
|
IsHosted: isHosted,
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidURLError := fmt.Errorf("%q: %w", u.String(), ErrInvalidGitHubConfigURL)
|
||||||
|
|
||||||
|
pathParts := strings.Split(strings.TrimPrefix(u.Path, "/"), "/")
|
||||||
|
|
||||||
|
switch len(pathParts) {
|
||||||
|
case 1: // Organization
|
||||||
|
if pathParts[0] == "" {
|
||||||
|
return nil, invalidURLError
|
||||||
|
}
|
||||||
|
|
||||||
|
configURL.Scope = GitHubScopeOrganization
|
||||||
|
configURL.Organization = pathParts[0]
|
||||||
|
|
||||||
|
case 2: // Repository or enterprise
|
||||||
|
if strings.ToLower(pathParts[0]) == "enterprises" {
|
||||||
|
configURL.Scope = GitHubScopeEnterprise
|
||||||
|
configURL.Enterprise = pathParts[1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
configURL.Scope = GitHubScopeRepository
|
||||||
|
configURL.Organization = pathParts[0]
|
||||||
|
configURL.Repository = pathParts[1]
|
||||||
|
default:
|
||||||
|
return nil, invalidURLError
|
||||||
|
}
|
||||||
|
|
||||||
|
return configURL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GitHubConfig) GitHubAPIURL(path string) *url.URL {
|
||||||
|
result := &url.URL{
|
||||||
|
Scheme: c.ConfigURL.Scheme,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch c.ConfigURL.Host {
|
||||||
|
// Hosted
|
||||||
|
case "github.com", "github.localhost":
|
||||||
|
result.Host = fmt.Sprintf("api.%s", c.ConfigURL.Host)
|
||||||
|
// re-routing www.github.com to api.github.com
|
||||||
|
case "www.github.com":
|
||||||
|
result.Host = "api.github.com"
|
||||||
|
|
||||||
|
// Enterprise
|
||||||
|
default:
|
||||||
|
result.Host = c.ConfigURL.Host
|
||||||
|
result.Path = "/api/v3"
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Path += path
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
package actions_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/actions/actions-runner-controller/github/actions"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGitHubConfig(t *testing.T) {
|
||||||
|
t.Run("when given a valid URL", func(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
configURL string
|
||||||
|
expected *actions.GitHubConfig
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
configURL: "https://github.com/org/repo",
|
||||||
|
expected: &actions.GitHubConfig{
|
||||||
|
Scope: actions.GitHubScopeRepository,
|
||||||
|
Enterprise: "",
|
||||||
|
Organization: "org",
|
||||||
|
Repository: "repo",
|
||||||
|
IsHosted: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
configURL: "https://github.com/org",
|
||||||
|
expected: &actions.GitHubConfig{
|
||||||
|
Scope: actions.GitHubScopeOrganization,
|
||||||
|
Enterprise: "",
|
||||||
|
Organization: "org",
|
||||||
|
Repository: "",
|
||||||
|
IsHosted: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
configURL: "https://github.com/enterprises/my-enterprise",
|
||||||
|
expected: &actions.GitHubConfig{
|
||||||
|
Scope: actions.GitHubScopeEnterprise,
|
||||||
|
Enterprise: "my-enterprise",
|
||||||
|
Organization: "",
|
||||||
|
Repository: "",
|
||||||
|
IsHosted: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
configURL: "https://www.github.com/org",
|
||||||
|
expected: &actions.GitHubConfig{
|
||||||
|
Scope: actions.GitHubScopeOrganization,
|
||||||
|
Enterprise: "",
|
||||||
|
Organization: "org",
|
||||||
|
Repository: "",
|
||||||
|
IsHosted: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
configURL: "https://github.localhost/org",
|
||||||
|
expected: &actions.GitHubConfig{
|
||||||
|
Scope: actions.GitHubScopeOrganization,
|
||||||
|
Enterprise: "",
|
||||||
|
Organization: "org",
|
||||||
|
Repository: "",
|
||||||
|
IsHosted: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
configURL: "https://my-ghes.com/org",
|
||||||
|
expected: &actions.GitHubConfig{
|
||||||
|
Scope: actions.GitHubScopeOrganization,
|
||||||
|
Enterprise: "",
|
||||||
|
Organization: "org",
|
||||||
|
Repository: "",
|
||||||
|
IsHosted: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.configURL, func(t *testing.T) {
|
||||||
|
parsedURL, err := url.Parse(test.configURL)
|
||||||
|
require.NoError(t, err)
|
||||||
|
test.expected.ConfigURL = parsedURL
|
||||||
|
|
||||||
|
cfg, err := actions.ParseGitHubConfigFromURL(test.configURL)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, test.expected, cfg)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("when given an invalid URL", func(t *testing.T) {})
|
||||||
|
invalidURLs := []string{
|
||||||
|
"https://github.com/",
|
||||||
|
"https://github.com",
|
||||||
|
"https://github.com/some/random/path",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, u := range invalidURLs {
|
||||||
|
_, err := actions.ParseGitHubConfigFromURL(u)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.True(t, errors.Is(err, actions.ErrInvalidGitHubConfigURL))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitHubConfig_GitHubAPIURL(t *testing.T) {
|
||||||
|
t.Run("when hosted", func(t *testing.T) {
|
||||||
|
config, err := actions.ParseGitHubConfigFromURL("https://github.com/org/repo")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
result := config.GitHubAPIURL("/some/path")
|
||||||
|
assert.Equal(t, "https://api.github.com/some/path", result.String())
|
||||||
|
})
|
||||||
|
t.Run("when not hosted", func(t *testing.T) {})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
package actions_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/actions/actions-runner-controller/github/actions"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewGitHubAPIRequest(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
t.Run("uses the right host/path prefix", func(t *testing.T) {
|
||||||
|
scenarios := []struct {
|
||||||
|
configURL string
|
||||||
|
path string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
configURL: "https://github.com/org/repo",
|
||||||
|
path: "/app/installations/123/access_tokens",
|
||||||
|
expected: "https://api.github.com/app/installations/123/access_tokens",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
configURL: "https://www.github.com/org/repo",
|
||||||
|
path: "/app/installations/123/access_tokens",
|
||||||
|
expected: "https://api.github.com/app/installations/123/access_tokens",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
configURL: "http://github.localhost/org/repo",
|
||||||
|
path: "/app/installations/123/access_tokens",
|
||||||
|
expected: "http://api.github.localhost/app/installations/123/access_tokens",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
configURL: "https://my-instance.com/org/repo",
|
||||||
|
path: "/app/installations/123/access_tokens",
|
||||||
|
expected: "https://my-instance.com/api/v3/app/installations/123/access_tokens",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
configURL: "http://localhost/org/repo",
|
||||||
|
path: "/app/installations/123/access_tokens",
|
||||||
|
expected: "http://localhost/api/v3/app/installations/123/access_tokens",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, scenario := range scenarios {
|
||||||
|
client, err := actions.NewClient(scenario.configURL, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
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 := actions.NewClient("http://localhost/my-org", nil, actions.WithUserAgent("my-agent"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
req, err := client.NewGitHubAPIRequest(ctx, http.MethodGet, "/app/installations/123/access_tokens", nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, "my-agent", req.Header.Get("User-Agent"))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("sets the body we pass", func(t *testing.T) {
|
||||||
|
client, err := actions.NewClient("http://localhost/my-org", nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
req, err := client.NewGitHubAPIRequest(
|
||||||
|
ctx,
|
||||||
|
http.MethodGet,
|
||||||
|
"/app/installations/123/access_tokens",
|
||||||
|
strings.NewReader("the-body"),
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
b, err := io.ReadAll(req.Body)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "the-body", string(b))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewActionsServiceRequest(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
defaultCreds := &actions.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 := newActionsServer(t, nil, withActionsToken(token))
|
||||||
|
|
||||||
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), defaultCreds)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
req, err := client.NewActionsServiceRequest(ctx, http.MethodGet, "my-path", nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, "Bearer "+token, req.Header.Get("Authorization"))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("admin token is about to expire", func(t *testing.T) {
|
||||||
|
newToken := defaultActionsToken(t)
|
||||||
|
server := newActionsServer(t, nil, withActionsToken(newToken))
|
||||||
|
|
||||||
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), defaultCreds)
|
||||||
|
require.NoError(t, err)
|
||||||
|
client.ActionsServiceAdminToken = "expiring-token"
|
||||||
|
client.ActionsServiceAdminTokenExpiresAt = time.Now().Add(59 * time.Second)
|
||||||
|
|
||||||
|
req, err := client.NewActionsServiceRequest(ctx, http.MethodGet, "my-path", nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, "Bearer "+newToken, req.Header.Get("Authorization"))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("token is currently valid", func(t *testing.T) {
|
||||||
|
tokenThatShouldNotBeFetched := defaultActionsToken(t)
|
||||||
|
server := newActionsServer(t, nil, withActionsToken(tokenThatShouldNotBeFetched))
|
||||||
|
|
||||||
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), defaultCreds)
|
||||||
|
require.NoError(t, err)
|
||||||
|
client.ActionsServiceAdminToken = "healthy-token"
|
||||||
|
client.ActionsServiceAdminTokenExpiresAt = time.Now().Add(1 * time.Hour)
|
||||||
|
|
||||||
|
req, err := client.NewActionsServiceRequest(ctx, http.MethodGet, "my-path", nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, "Bearer healthy-token", req.Header.Get("Authorization"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("builds the right URL including api version", func(t *testing.T) {
|
||||||
|
server := newActionsServer(t, nil)
|
||||||
|
|
||||||
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), defaultCreds)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
req, err := client.NewActionsServiceRequest(ctx, http.MethodGet, "/my/path?name=banana", nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
serverURL, err := url.Parse(server.URL)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
result := req.URL
|
||||||
|
assert.Equal(t, serverURL.Host, result.Host)
|
||||||
|
assert.Equal(t, "/tenant/123/my/path", result.Path)
|
||||||
|
assert.Equal(t, "banana", result.Query().Get("name"))
|
||||||
|
assert.Equal(t, "6.0-preview", result.Query().Get("api-version"))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("populates header", func(t *testing.T) {
|
||||||
|
server := newActionsServer(t, nil)
|
||||||
|
|
||||||
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), defaultCreds, actions.WithUserAgent("my-agent"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
req, err := client.NewActionsServiceRequest(ctx, http.MethodGet, "/my/path", nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, "my-agent", req.Header.Get("User-Agent"))
|
||||||
|
assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -106,7 +106,6 @@ func (m *multiClient) GetClientFor(ctx context.Context, githubConfigURL string,
|
||||||
m.logger.Info("creating new client", "githubConfigURL", githubConfigURL, "namespace", namespace)
|
m.logger.Info("creating new client", "githubConfigURL", githubConfigURL, "namespace", namespace)
|
||||||
|
|
||||||
client, err := NewClient(
|
client, err := NewClient(
|
||||||
ctx,
|
|
||||||
githubConfigURL,
|
githubConfigURL,
|
||||||
&creds,
|
&creds,
|
||||||
WithUserAgent(m.userAgent),
|
WithUserAgent(m.userAgent),
|
||||||
|
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
package actions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGithubAPIURL(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
configURL string
|
|
||||||
path string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
configURL: "https://github.com/org/repo",
|
|
||||||
path: "/app/installations/123/access_tokens",
|
|
||||||
expected: "https://api.github.com/app/installations/123/access_tokens",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
configURL: "https://www.github.com/org/repo",
|
|
||||||
path: "/app/installations/123/access_tokens",
|
|
||||||
expected: "https://api.github.com/app/installations/123/access_tokens",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
configURL: "http://github.localhost/org/repo",
|
|
||||||
path: "/app/installations/123/access_tokens",
|
|
||||||
expected: "http://api.github.localhost/app/installations/123/access_tokens",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
configURL: "https://my-instance.com/org/repo",
|
|
||||||
path: "/app/installations/123/access_tokens",
|
|
||||||
expected: "https://my-instance.com/api/v3/app/installations/123/access_tokens",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
configURL: "http://localhost/org/repo",
|
|
||||||
path: "/app/installations/123/access_tokens",
|
|
||||||
expected: "http://localhost/api/v3/app/installations/123/access_tokens",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
actual, err := githubAPIURL(test.configURL, test.path)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue