support for organization runners
This commit is contained in:
		
							parent
							
								
									d1429beaa6
								
							
						
					
					
						commit
						fb35dd4131
					
				|  | @ -24,7 +24,11 @@ import ( | ||||||
| // RunnerSpec defines the desired state of Runner
 | // RunnerSpec defines the desired state of Runner
 | ||||||
| type RunnerSpec struct { | type RunnerSpec struct { | ||||||
| 	// +kubebuilder:validation:MinLength=3
 | 	// +kubebuilder:validation:MinLength=3
 | ||||||
| 	// +kubebuilder:validation:Pattern=`^[^/]+/[^/]+$`
 | 	// +kubebuilder:validation:Pattern=`^[^/]+$`
 | ||||||
|  | 	Organization string `json:"organization"` | ||||||
|  | 
 | ||||||
|  | 	// +optional
 | ||||||
|  | 	// +kubebuilder:validation:Pattern=`^[^/]*$`
 | ||||||
| 	Repository string `json:"repository"` | 	Repository string `json:"repository"` | ||||||
| 
 | 
 | ||||||
| 	// +optional
 | 	// +optional
 | ||||||
|  |  | ||||||
|  | @ -4115,9 +4115,12 @@ spec: | ||||||
|                       additionalProperties: |                       additionalProperties: | ||||||
|                         type: string |                         type: string | ||||||
|                       type: object |                       type: object | ||||||
|                     repository: |                     organization: | ||||||
|                       minLength: 3 |                       minLength: 3 | ||||||
|                       pattern: ^[^/]+/[^/]+$ |                       pattern: ^[^/]+$ | ||||||
|  |                       type: string | ||||||
|  |                     repository: | ||||||
|  |                       pattern: ^[^/]*$ | ||||||
|                       type: string |                       type: string | ||||||
|                     resources: |                     resources: | ||||||
|                       description: ResourceRequirements describes the compute resource |                       description: ResourceRequirements describes the compute resource | ||||||
|  | @ -6709,7 +6712,7 @@ spec: | ||||||
|                         type: object |                         type: object | ||||||
|                       type: array |                       type: array | ||||||
|                   required: |                   required: | ||||||
|                   - repository |                   - organization | ||||||
|                   type: object |                   type: object | ||||||
|               type: object |               type: object | ||||||
|           required: |           required: | ||||||
|  |  | ||||||
|  | @ -4115,9 +4115,12 @@ spec: | ||||||
|                       additionalProperties: |                       additionalProperties: | ||||||
|                         type: string |                         type: string | ||||||
|                       type: object |                       type: object | ||||||
|                     repository: |                     organization: | ||||||
|                       minLength: 3 |                       minLength: 3 | ||||||
|                       pattern: ^[^/]+/[^/]+$ |                       pattern: ^[^/]+$ | ||||||
|  |                       type: string | ||||||
|  |                     repository: | ||||||
|  |                       pattern: ^[^/]*$ | ||||||
|                       type: string |                       type: string | ||||||
|                     resources: |                     resources: | ||||||
|                       description: ResourceRequirements describes the compute resource |                       description: ResourceRequirements describes the compute resource | ||||||
|  | @ -6709,7 +6712,7 @@ spec: | ||||||
|                         type: object |                         type: object | ||||||
|                       type: array |                       type: array | ||||||
|                   required: |                   required: | ||||||
|                   - repository |                   - organization | ||||||
|                   type: object |                   type: object | ||||||
|               type: object |               type: object | ||||||
|           required: |           required: | ||||||
|  |  | ||||||
|  | @ -3858,9 +3858,12 @@ spec: | ||||||
|               additionalProperties: |               additionalProperties: | ||||||
|                 type: string |                 type: string | ||||||
|               type: object |               type: object | ||||||
|             repository: |             organization: | ||||||
|               minLength: 3 |               minLength: 3 | ||||||
|               pattern: ^[^/]+/[^/]+$ |               pattern: ^[^/]+$ | ||||||
|  |               type: string | ||||||
|  |             repository: | ||||||
|  |               pattern: ^[^/]*$ | ||||||
|               type: string |               type: string | ||||||
|             resources: |             resources: | ||||||
|               description: ResourceRequirements describes the compute resource requirements. |               description: ResourceRequirements describes the compute resource requirements. | ||||||
|  | @ -6293,7 +6296,7 @@ spec: | ||||||
|                 type: object |                 type: object | ||||||
|               type: array |               type: array | ||||||
|           required: |           required: | ||||||
|           - repository |           - organization | ||||||
|           type: object |           type: object | ||||||
|         status: |         status: | ||||||
|           description: RunnerStatus defines the observed state of Runner |           description: RunnerStatus defines the observed state of Runner | ||||||
|  |  | ||||||
|  | @ -83,7 +83,7 @@ func (r *RunnerReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { | ||||||
| 		finalizers, removed := removeFinalizer(runner.ObjectMeta.Finalizers) | 		finalizers, removed := removeFinalizer(runner.ObjectMeta.Finalizers) | ||||||
| 
 | 
 | ||||||
| 		if removed { | 		if removed { | ||||||
| 			ok, err := r.unregisterRunner(ctx, runner.Spec.Repository, runner.Name) | 			ok, err := r.unregisterRunner(ctx, runner.Spec.Organization, runner.Spec.Repository, runner.Name) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Error(err, "Failed to unregister runner") | 				log.Error(err, "Failed to unregister runner") | ||||||
| 				return ctrl.Result{}, err | 				return ctrl.Result{}, err | ||||||
|  | @ -108,7 +108,7 @@ func (r *RunnerReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if !runner.IsRegisterable() { | 	if !runner.IsRegisterable() { | ||||||
| 		rt, err := r.GitHubClient.GetRegistrationToken(ctx, runner.Spec.Repository, runner.Name) | 		rt, err := r.GitHubClient.GetRegistrationToken(ctx, runner.Spec.Organization, runner.Spec.Repository, runner.Name) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			r.Recorder.Event(&runner, corev1.EventTypeWarning, "FailedUpdateRegistrationToken", "Updating registration token failed") | 			r.Recorder.Event(&runner, corev1.EventTypeWarning, "FailedUpdateRegistrationToken", "Updating registration token failed") | ||||||
| 			log.Error(err, "Failed to get new registration token") | 			log.Error(err, "Failed to get new registration token") | ||||||
|  | @ -212,8 +212,8 @@ func (r *RunnerReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { | ||||||
| 	return ctrl.Result{}, nil | 	return ctrl.Result{}, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (r *RunnerReconciler) unregisterRunner(ctx context.Context, repo, name string) (bool, error) { | func (r *RunnerReconciler) unregisterRunner(ctx context.Context, org, repo, name string) (bool, error) { | ||||||
| 	runners, err := r.GitHubClient.ListRunners(ctx, repo) | 	runners, err := r.GitHubClient.ListRunners(ctx, org, repo) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return false, err | 		return false, err | ||||||
| 	} | 	} | ||||||
|  | @ -230,7 +230,7 @@ func (r *RunnerReconciler) unregisterRunner(ctx context.Context, repo, name stri | ||||||
| 		return false, nil | 		return false, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := r.GitHubClient.RemoveRunner(ctx, repo, id); err != nil { | 	if err := r.GitHubClient.RemoveRunner(ctx, org, repo, id); err != nil { | ||||||
| 		return false, err | 		return false, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -253,6 +253,10 @@ func (r *RunnerReconciler) newPod(runner v1alpha1.Runner) (corev1.Pod, error) { | ||||||
| 			Name:  "RUNNER_NAME", | 			Name:  "RUNNER_NAME", | ||||||
| 			Value: runner.Name, | 			Value: runner.Name, | ||||||
| 		}, | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Name:  "RUNNER_ORG", | ||||||
|  | 			Value: runner.Spec.Organization, | ||||||
|  | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			Name:  "RUNNER_REPO", | 			Name:  "RUNNER_REPO", | ||||||
| 			Value: runner.Spec.Repository, | 			Value: runner.Spec.Repository, | ||||||
|  |  | ||||||
|  | @ -31,6 +31,7 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { | ||||||
| 	fmt.Fprintf(w, h.Body) | 	fmt.Fprintf(w, h.Body) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // NewServer creates a fake server for running unit tests
 | ||||||
| func NewServer() *httptest.Server { | func NewServer() *httptest.Server { | ||||||
| 	routes := map[string]handler{ | 	routes := map[string]handler{ | ||||||
| 		// For CreateRegistrationToken
 | 		// For CreateRegistrationToken
 | ||||||
|  | @ -46,6 +47,18 @@ func NewServer() *httptest.Server { | ||||||
| 			Status: http.StatusBadRequest, | 			Status: http.StatusBadRequest, | ||||||
| 			Body:   "", | 			Body:   "", | ||||||
| 		}, | 		}, | ||||||
|  | 		"/orgs/test/actions/runners/registration-token": handler{ | ||||||
|  | 			Status: http.StatusCreated, | ||||||
|  | 			Body:   fmt.Sprintf("{\"token\": \"%s\", \"expires_at\": \"%s\"}", RegistrationToken, time.Now().Add(time.Hour*1).Format(time.RFC3339)), | ||||||
|  | 		}, | ||||||
|  | 		"/orgs/invalid/actions/runners/registration-token": handler{ | ||||||
|  | 			Status: http.StatusOK, | ||||||
|  | 			Body:   fmt.Sprintf("{\"token\": \"%s\", \"expires_at\": \"%s\"}", RegistrationToken, time.Now().Add(time.Hour*1).Format(time.RFC3339)), | ||||||
|  | 		}, | ||||||
|  | 		"/orgs/error/actions/runners/registration-token": handler{ | ||||||
|  | 			Status: http.StatusBadRequest, | ||||||
|  | 			Body:   "", | ||||||
|  | 		}, | ||||||
| 
 | 
 | ||||||
| 		// For ListRunners
 | 		// For ListRunners
 | ||||||
| 		"/repos/test/valid/actions/runners": handler{ | 		"/repos/test/valid/actions/runners": handler{ | ||||||
|  | @ -60,6 +73,18 @@ func NewServer() *httptest.Server { | ||||||
| 			Status: http.StatusBadRequest, | 			Status: http.StatusBadRequest, | ||||||
| 			Body:   "", | 			Body:   "", | ||||||
| 		}, | 		}, | ||||||
|  | 		"/orgs/test/actions/runners": handler{ | ||||||
|  | 			Status: http.StatusOK, | ||||||
|  | 			Body:   RunnersListBody, | ||||||
|  | 		}, | ||||||
|  | 		"/orgs/invalid/actions/runners": handler{ | ||||||
|  | 			Status: http.StatusNoContent, | ||||||
|  | 			Body:   "", | ||||||
|  | 		}, | ||||||
|  | 		"/orgs/error/actions/runners": handler{ | ||||||
|  | 			Status: http.StatusBadRequest, | ||||||
|  | 			Body:   "", | ||||||
|  | 		}, | ||||||
| 
 | 
 | ||||||
| 		// For RemoveRunner
 | 		// For RemoveRunner
 | ||||||
| 		"/repos/test/valid/actions/runners/1": handler{ | 		"/repos/test/valid/actions/runners/1": handler{ | ||||||
|  | @ -74,6 +99,18 @@ func NewServer() *httptest.Server { | ||||||
| 			Status: http.StatusBadRequest, | 			Status: http.StatusBadRequest, | ||||||
| 			Body:   "", | 			Body:   "", | ||||||
| 		}, | 		}, | ||||||
|  | 		"/orgs/test/actions/runners/1": handler{ | ||||||
|  | 			Status: http.StatusNoContent, | ||||||
|  | 			Body:   "", | ||||||
|  | 		}, | ||||||
|  | 		"/orgs/invalid/actions/runners/1": handler{ | ||||||
|  | 			Status: http.StatusOK, | ||||||
|  | 			Body:   "", | ||||||
|  | 		}, | ||||||
|  | 		"/orgs/error/actions/runners/1": handler{ | ||||||
|  | 			Status: http.StatusBadRequest, | ||||||
|  | 			Body:   "", | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	mux := http.NewServeMux() | 	mux := http.NewServeMux() | ||||||
|  |  | ||||||
|  | @ -2,10 +2,8 @@ package github | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"errors" |  | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"strings" |  | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
|  | @ -14,6 +12,7 @@ import ( | ||||||
| 	"golang.org/x/oauth2" | 	"golang.org/x/oauth2" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // Client wraps GitHub client with some additional
 | ||||||
| type Client struct { | type Client struct { | ||||||
| 	*github.Client | 	*github.Client | ||||||
| 	regTokens map[string]*github.RegistrationToken | 	regTokens map[string]*github.RegistrationToken | ||||||
|  | @ -34,7 +33,7 @@ func NewClient(appID, installationID int64, privateKeyPath string) (*Client, err | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewClient returns a client authenticated with personal access token.
 | // NewClientWithAccessToken returns a client authenticated with personal access token.
 | ||||||
| func NewClientWithAccessToken(token string) (*Client, error) { | func NewClientWithAccessToken(token string) (*Client, error) { | ||||||
| 	tc := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource( | 	tc := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource( | ||||||
| 		&oauth2.Token{AccessToken: token}, | 		&oauth2.Token{AccessToken: token}, | ||||||
|  | @ -48,22 +47,31 @@ func NewClientWithAccessToken(token string) (*Client, error) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetRegistrationToken returns a registration token tied with the name of repository and runner.
 | // GetRegistrationToken returns a registration token tied with the name of repository and runner.
 | ||||||
| func (c *Client) GetRegistrationToken(ctx context.Context, repository, name string) (*github.RegistrationToken, error) { | func (c *Client) GetRegistrationToken(ctx context.Context, owner, repo, name string) (*github.RegistrationToken, error) { | ||||||
| 	c.mu.Lock() | 	c.mu.Lock() | ||||||
| 	defer c.mu.Unlock() | 	defer c.mu.Unlock() | ||||||
| 
 | 
 | ||||||
| 	owner, repo, err := splitOwnerAndRepo(repository) | 	key := owner | ||||||
| 	if err != nil { | 	if len(repo) > 0 { | ||||||
| 		return nil, err | 		key = fmt.Sprintf("%s/%s", repo, name) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	key := fmt.Sprintf("%s/%s", repo, name) | 	var rt *github.RegistrationToken | ||||||
|  | 
 | ||||||
| 	rt, ok := c.regTokens[key] | 	rt, ok := c.regTokens[key] | ||||||
| 	if ok && rt.GetExpiresAt().After(time.Now().Add(-10*time.Minute)) { | 	if ok && rt.GetExpiresAt().After(time.Now().Add(-10*time.Minute)) { | ||||||
| 		return rt, nil | 		return rt, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	rt, res, err := c.Client.Actions.CreateRegistrationToken(ctx, owner, repo) | 	var res *github.Response | ||||||
|  | 	var err error | ||||||
|  | 
 | ||||||
|  | 	if len(repo) > 0 { | ||||||
|  | 		rt, res, err = c.Client.Actions.CreateRegistrationToken(ctx, owner, repo) | ||||||
|  | 	} else { | ||||||
|  | 		rt, res, err = CreateOrganizationRegistrationToken(ctx, c, owner) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("failed to create registration token: %v", err) | 		return nil, fmt.Errorf("failed to create registration token: %v", err) | ||||||
| 	} | 	} | ||||||
|  | @ -81,13 +89,16 @@ func (c *Client) GetRegistrationToken(ctx context.Context, repository, name stri | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // RemoveRunner removes a runner with specified runner ID from repocitory.
 | // RemoveRunner removes a runner with specified runner ID from repocitory.
 | ||||||
| func (c *Client) RemoveRunner(ctx context.Context, repository string, runnerID int64) error { | func (c *Client) RemoveRunner(ctx context.Context, owner, repo string, runnerID int64) error { | ||||||
| 	owner, repo, err := splitOwnerAndRepo(repository) | 	var res *github.Response | ||||||
| 	if err != nil { | 	var err error | ||||||
| 		return err | 
 | ||||||
|  | 	if len(repo) > 0 { | ||||||
|  | 		res, err = c.Client.Actions.RemoveRunner(ctx, owner, repo, runnerID) | ||||||
|  | 	} else { | ||||||
|  | 		res, err = RemoveOrganizationRunner(ctx, c, owner, runnerID) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	res, err := c.Client.Actions.RemoveRunner(ctx, owner, repo, runnerID) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("failed to remove runner: %v", err) | 		return fmt.Errorf("failed to remove runner: %v", err) | ||||||
| 	} | 	} | ||||||
|  | @ -99,18 +110,22 @@ func (c *Client) RemoveRunner(ctx context.Context, repository string, runnerID i | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ListRunners returns a list of runners of specified repository name.
 | // ListRunners returns a list of runners of specified owner/repository name.
 | ||||||
| func (c *Client) ListRunners(ctx context.Context, repository string) ([]*github.Runner, error) { | func (c *Client) ListRunners(ctx context.Context, owner, repo string) ([]*github.Runner, error) { | ||||||
| 	var runners []*github.Runner | 	var runners []*github.Runner | ||||||
| 
 | 
 | ||||||
| 	owner, repo, err := splitOwnerAndRepo(repository) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return runners, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	opts := github.ListOptions{PerPage: 10} | 	opts := github.ListOptions{PerPage: 10} | ||||||
| 	for { | 	for { | ||||||
| 		list, res, err := c.Client.Actions.ListRunners(ctx, owner, repo, &opts) | 		list := &github.Runners{} | ||||||
|  | 		var res *github.Response | ||||||
|  | 		var err error | ||||||
|  | 
 | ||||||
|  | 		if len(repo) > 0 { | ||||||
|  | 			list, res, err = c.Client.Actions.ListRunners(ctx, owner, repo, &opts) | ||||||
|  | 		} else { | ||||||
|  | 			list, res, err = ListOrganizationRunners(ctx, c, owner, &opts) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return runners, fmt.Errorf("failed to remove runner: %v", err) | 			return runners, fmt.Errorf("failed to remove runner: %v", err) | ||||||
| 		} | 		} | ||||||
|  | @ -136,12 +151,3 @@ func (c *Client) cleanup() { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 |  | ||||||
| // splitOwnerAndRepo splits specified repository name to the owner and repo name.
 |  | ||||||
| func splitOwnerAndRepo(repo string) (string, string, error) { |  | ||||||
| 	chunk := strings.Split(repo, "/") |  | ||||||
| 	if len(chunk) != 2 { |  | ||||||
| 		return "", "", errors.New("invalid repository name") |  | ||||||
| 	} |  | ||||||
| 	return chunk[0], chunk[1], nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -0,0 +1,95 @@ | ||||||
|  | package github | ||||||
|  | 
 | ||||||
|  | // this contains BETA API clients, that are currently not (yet) in go-github
 | ||||||
|  | // once these functions have been added there, they can be removed from here
 | ||||||
|  | // code was reused from https://github.com/google/go-github
 | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/url" | ||||||
|  | 	"reflect" | ||||||
|  | 
 | ||||||
|  | 	"github.com/google/go-github/v31/github" | ||||||
|  | 	"github.com/google/go-querystring/query" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // CreateOrganizationRegistrationToken creates a token that can be used to add a self-hosted runner on an organization.
 | ||||||
|  | //
 | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/actions/self-hosted-runners/#create-a-registration-token-for-an-organization
 | ||||||
|  | func CreateOrganizationRegistrationToken(ctx context.Context, client *Client, owner string) (*github.RegistrationToken, *github.Response, error) { | ||||||
|  | 	u := fmt.Sprintf("orgs/%v/actions/runners/registration-token", owner) | ||||||
|  | 
 | ||||||
|  | 	req, err := client.NewRequest("POST", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	registrationToken := new(github.RegistrationToken) | ||||||
|  | 	resp, err := client.Do(ctx, req, registrationToken) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return registrationToken, resp, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ListOrganizationRunners lists all the self-hosted runners for an organization.
 | ||||||
|  | //
 | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/actions/self-hosted-runners/#list-self-hosted-runners-for-an-organization
 | ||||||
|  | func ListOrganizationRunners(ctx context.Context, client *Client, owner string, opts *github.ListOptions) (*github.Runners, *github.Response, error) { | ||||||
|  | 	u := fmt.Sprintf("orgs/%v/actions/runners", owner) | ||||||
|  | 	u, err := addOptions(u, opts) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	req, err := client.NewRequest("GET", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	runners := &github.Runners{} | ||||||
|  | 	resp, err := client.Do(ctx, req, &runners) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, resp, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return runners, resp, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // RemoveOrganizationRunner forces the removal of a self-hosted runner in a repository using the runner id.
 | ||||||
|  | //
 | ||||||
|  | // GitHub API docs: https://developer.github.com/v3/actions/self_hosted_runners/#remove-a-self-hosted-runner
 | ||||||
|  | func RemoveOrganizationRunner(ctx context.Context, client *Client, owner string, runnerID int64) (*github.Response, error) { | ||||||
|  | 	u := fmt.Sprintf("orgs/%v/actions/runners/%v", owner, runnerID) | ||||||
|  | 
 | ||||||
|  | 	req, err := client.NewRequest("DELETE", u, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return client.Do(ctx, req, nil) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // addOptions adds the parameters in opt as URL query parameters to s. opt
 | ||||||
|  | // must be a struct whose fields may contain "url" tags.
 | ||||||
|  | func addOptions(s string, opts interface{}) (string, error) { | ||||||
|  | 	v := reflect.ValueOf(opts) | ||||||
|  | 	if v.Kind() == reflect.Ptr && v.IsNil() { | ||||||
|  | 		return s, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	u, err := url.Parse(s) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return s, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	qs, err := query.Values(opts) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return s, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	u.RawQuery = qs.Encode() | ||||||
|  | 	return u.String(), nil | ||||||
|  | } | ||||||
|  | @ -36,18 +36,22 @@ func TestMain(m *testing.M) { | ||||||
| 
 | 
 | ||||||
| func TestGetRegistrationToken(t *testing.T) { | func TestGetRegistrationToken(t *testing.T) { | ||||||
| 	tests := []struct { | 	tests := []struct { | ||||||
|  | 		org   string | ||||||
| 		repo  string | 		repo  string | ||||||
| 		token string | 		token string | ||||||
| 		err   bool | 		err   bool | ||||||
| 	}{ | 	}{ | ||||||
| 		{repo: "test/valid", token: fake.RegistrationToken, err: false}, | 		{org: "test", repo: "valid", token: fake.RegistrationToken, err: false}, | ||||||
| 		{repo: "test/invalid", token: "", err: true}, | 		{org: "test", repo: "invalid", token: "", err: true}, | ||||||
| 		{repo: "test/error", token: "", err: true}, | 		{org: "test", repo: "error", token: "", err: true}, | ||||||
|  | 		{org: "test", repo: "", token: fake.RegistrationToken, err: false}, | ||||||
|  | 		{org: "invalid", repo: "", token: "", err: true}, | ||||||
|  | 		{org: "error", repo: "", token: "", err: true}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	client := newTestClient() | 	client := newTestClient() | ||||||
| 	for i, tt := range tests { | 	for i, tt := range tests { | ||||||
| 		rt, err := client.GetRegistrationToken(context.Background(), tt.repo, "test") | 		rt, err := client.GetRegistrationToken(context.Background(), tt.org, tt.repo, "test") | ||||||
| 		if !tt.err && err != nil { | 		if !tt.err && err != nil { | ||||||
| 			t.Errorf("[%d] unexpected error: %v", i, err) | 			t.Errorf("[%d] unexpected error: %v", i, err) | ||||||
| 		} | 		} | ||||||
|  | @ -59,18 +63,22 @@ func TestGetRegistrationToken(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| func TestListRunners(t *testing.T) { | func TestListRunners(t *testing.T) { | ||||||
| 	tests := []struct { | 	tests := []struct { | ||||||
|  | 		org    string | ||||||
| 		repo   string | 		repo   string | ||||||
| 		length int | 		length int | ||||||
| 		err    bool | 		err    bool | ||||||
| 	}{ | 	}{ | ||||||
| 		{repo: "test/valid", length: 2, err: false}, | 		{org: "test", repo: "valid", length: 2, err: false}, | ||||||
| 		{repo: "test/invalid", length: 0, err: true}, | 		{org: "test", repo: "invalid", length: 0, err: true}, | ||||||
| 		{repo: "test/error", length: 0, err: true}, | 		{org: "test", repo: "error", length: 0, err: true}, | ||||||
|  | 		{org: "test", repo: "", length: 2, err: false}, | ||||||
|  | 		{org: "invalid", repo: "", length: 0, err: true}, | ||||||
|  | 		{org: "error", repo: "", length: 0, err: true}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	client := newTestClient() | 	client := newTestClient() | ||||||
| 	for i, tt := range tests { | 	for i, tt := range tests { | ||||||
| 		runners, err := client.ListRunners(context.Background(), tt.repo) | 		runners, err := client.ListRunners(context.Background(), tt.org, tt.repo) | ||||||
| 		if !tt.err && err != nil { | 		if !tt.err && err != nil { | ||||||
| 			t.Errorf("[%d] unexpected error: %v", i, err) | 			t.Errorf("[%d] unexpected error: %v", i, err) | ||||||
| 		} | 		} | ||||||
|  | @ -82,17 +90,21 @@ func TestListRunners(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| func TestRemoveRunner(t *testing.T) { | func TestRemoveRunner(t *testing.T) { | ||||||
| 	tests := []struct { | 	tests := []struct { | ||||||
|  | 		org  string | ||||||
| 		repo string | 		repo string | ||||||
| 		err  bool | 		err  bool | ||||||
| 	}{ | 	}{ | ||||||
| 		{repo: "test/valid", err: false}, | 		{org: "test", repo: "valid", err: false}, | ||||||
| 		{repo: "test/invalid", err: true}, | 		{org: "test", repo: "invalid", err: true}, | ||||||
| 		{repo: "test/error", err: true}, | 		{org: "test", repo: "error", err: true}, | ||||||
|  | 		{org: "test", repo: "", err: false}, | ||||||
|  | 		{org: "invalid", repo: "", err: true}, | ||||||
|  | 		{org: "error", repo: "", err: true}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	client := newTestClient() | 	client := newTestClient() | ||||||
| 	for i, tt := range tests { | 	for i, tt := range tests { | ||||||
| 		err := client.RemoveRunner(context.Background(), tt.repo, int64(1)) | 		err := client.RemoveRunner(context.Background(), tt.org, tt.repo, int64(1)) | ||||||
| 		if !tt.err && err != nil { | 		if !tt.err && err != nil { | ||||||
| 			t.Errorf("[%d] unexpected error: %v", i, err) | 			t.Errorf("[%d] unexpected error: %v", i, err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										1
									
								
								go.mod
								
								
								
								
							|  | @ -7,6 +7,7 @@ require ( | ||||||
| 	github.com/davecgh/go-spew v1.1.1 | 	github.com/davecgh/go-spew v1.1.1 | ||||||
| 	github.com/go-logr/logr v0.1.0 | 	github.com/go-logr/logr v0.1.0 | ||||||
| 	github.com/google/go-github/v31 v31.0.0 | 	github.com/google/go-github/v31 v31.0.0 | ||||||
|  | 	github.com/google/go-querystring v1.0.0 | ||||||
| 	github.com/onsi/ginkgo v1.8.0 | 	github.com/onsi/ginkgo v1.8.0 | ||||||
| 	github.com/onsi/gomega v1.5.0 | 	github.com/onsi/gomega v1.5.0 | ||||||
| 	github.com/stretchr/testify v1.4.0 // indirect | 	github.com/stretchr/testify v1.4.0 // indirect | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										1
									
								
								go.sum
								
								
								
								
							|  | @ -116,6 +116,7 @@ github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= | ||||||
| github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||||
| github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= | github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= | ||||||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||||
|  | github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= | ||||||
| github.com/google/go-github/v29 v29.0.2 h1:opYN6Wc7DOz7Ku3Oh4l7prmkOMwEcQxpFtxdU8N8Pts= | github.com/google/go-github/v29 v29.0.2 h1:opYN6Wc7DOz7Ku3Oh4l7prmkOMwEcQxpFtxdU8N8Pts= | ||||||
| github.com/google/go-github/v29 v29.0.2/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E= | github.com/google/go-github/v29 v29.0.2/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E= | ||||||
| github.com/google/go-github/v31 v31.0.0 h1:JJUxlP9lFK+ziXKimTCprajMApV1ecWD4NB6CCb0plo= | github.com/google/go-github/v31 v31.0.0 h1:JJUxlP9lFK+ziXKimTCprajMApV1ecWD4NB6CCb0plo= | ||||||
|  |  | ||||||
|  | @ -5,18 +5,24 @@ if [ -z "${RUNNER_NAME}" ]; then | ||||||
|   exit 1 |   exit 1 | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| if [ -z "${RUNNER_REPO}" ]; then | if [ -z "${RUNNER_ORG}" ]; then | ||||||
|   echo "RUNNER_REPO must be set" 1>&2 |   echo "RUNNER_ORG must be set" 1>&2 | ||||||
|   exit 1 |   exit 1 | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
|  | ATTACH="${RUNNER_ORG}" | ||||||
|  | 
 | ||||||
|  | if [ ! -z "${RUNNER_REPO}" ]; then | ||||||
|  |   ATTACH="${RUNNER_ORG}/${RUNNER_REPO}" | ||||||
|  | fi | ||||||
|  | 
 | ||||||
| if [ -z "${RUNNER_TOKEN}" ]; then | if [ -z "${RUNNER_TOKEN}" ]; then | ||||||
|   echo "RUNNER_TOKEN must be set" 1>&2 |   echo "RUNNER_TOKEN must be set" 1>&2 | ||||||
|   exit 1 |   exit 1 | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| cd /runner | cd /runner | ||||||
| ./config.sh --unattended --replace --name "${RUNNER_NAME}" --url "https://github.com/${RUNNER_REPO}" --token "${RUNNER_TOKEN}" | ./config.sh --unattended --replace --name "${RUNNER_NAME}" --url "https://github.com/${ATTACH}" --token "${RUNNER_TOKEN}" | ||||||
| 
 | 
 | ||||||
| unset RUNNER_NAME RUNNER_REPO RUNNER_TOKEN | unset RUNNER_NAME RUNNER_REPO RUNNER_TOKEN | ||||||
| exec ./run.sh --once | exec ./run.sh --once | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue