Merge pull request #31 from summerwind/github-package
Use github package to access the GitHub API
This commit is contained in:
		
						commit
						5714459c24
					
				| 
						 | 
					@ -13,6 +13,7 @@ RUN go mod download
 | 
				
			||||||
COPY main.go main.go
 | 
					COPY main.go main.go
 | 
				
			||||||
COPY api/ api/
 | 
					COPY api/ api/
 | 
				
			||||||
COPY controllers/ controllers/
 | 
					COPY controllers/ controllers/
 | 
				
			||||||
 | 
					COPY github/ github/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Build
 | 
					# Build
 | 
				
			||||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go
 | 
					RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,10 +20,8 @@ import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/go-logr/logr"
 | 
						"github.com/go-logr/logr"
 | 
				
			||||||
	"github.com/google/go-github/v29/github"
 | 
					 | 
				
			||||||
	"k8s.io/apimachinery/pkg/api/errors"
 | 
						"k8s.io/apimachinery/pkg/api/errors"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
						"k8s.io/apimachinery/pkg/runtime"
 | 
				
			||||||
	"k8s.io/client-go/tools/record"
 | 
						"k8s.io/client-go/tools/record"
 | 
				
			||||||
| 
						 | 
					@ -34,6 +32,7 @@ import (
 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/summerwind/actions-runner-controller/api/v1alpha1"
 | 
						"github.com/summerwind/actions-runner-controller/api/v1alpha1"
 | 
				
			||||||
 | 
						"github.com/summerwind/actions-runner-controller/github"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
| 
						 | 
					@ -41,23 +40,6 @@ const (
 | 
				
			||||||
	finalizerName = "runner.actions.summerwind.dev"
 | 
						finalizerName = "runner.actions.summerwind.dev"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type GitHubRunnerList struct {
 | 
					 | 
				
			||||||
	TotalCount int            `json:"total_count"`
 | 
					 | 
				
			||||||
	Runners    []GitHubRunner `json:"runners,omitempty"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type GitHubRunner struct {
 | 
					 | 
				
			||||||
	ID     int    `json:"id"`
 | 
					 | 
				
			||||||
	Name   string `json:"name"`
 | 
					 | 
				
			||||||
	OS     string `json:"os"`
 | 
					 | 
				
			||||||
	Status string `json:"status"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type GitHubRegistrationToken struct {
 | 
					 | 
				
			||||||
	Token     string `json:"token"`
 | 
					 | 
				
			||||||
	ExpiresAt string `json:"expires_at"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// RunnerReconciler reconciles a Runner object
 | 
					// RunnerReconciler reconciles a Runner object
 | 
				
			||||||
type RunnerReconciler struct {
 | 
					type RunnerReconciler struct {
 | 
				
			||||||
	client.Client
 | 
						client.Client
 | 
				
			||||||
| 
						 | 
					@ -126,7 +108,7 @@ func (r *RunnerReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !runner.IsRegisterable() {
 | 
						if !runner.IsRegisterable() {
 | 
				
			||||||
		reg, err := r.newRegistration(ctx, runner.Spec.Repository)
 | 
							rt, err := r.GitHubClient.GetRegistrationToken(ctx, 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")
 | 
				
			||||||
| 
						 | 
					@ -134,7 +116,11 @@ func (r *RunnerReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		updated := runner.DeepCopy()
 | 
							updated := runner.DeepCopy()
 | 
				
			||||||
		updated.Status.Registration = reg
 | 
							updated.Status.Registration = v1alpha1.RunnerStatusRegistration{
 | 
				
			||||||
 | 
								Repository: runner.Spec.Repository,
 | 
				
			||||||
 | 
								Token:      rt.GetToken(),
 | 
				
			||||||
 | 
								ExpiresAt:  metav1.NewTime(rt.GetExpiresAt().Time),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err := r.Status().Update(ctx, updated); err != nil {
 | 
							if err := r.Status().Update(ctx, updated); err != nil {
 | 
				
			||||||
			log.Error(err, "Failed to update runner status")
 | 
								log.Error(err, "Failed to update runner status")
 | 
				
			||||||
| 
						 | 
					@ -226,109 +212,31 @@ func (r *RunnerReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 | 
				
			||||||
	return ctrl.Result{}, nil
 | 
						return ctrl.Result{}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *RunnerReconciler) newRegistration(ctx context.Context, repo string) (v1alpha1.RunnerStatusRegistration, error) {
 | 
					 | 
				
			||||||
	var reg v1alpha1.RunnerStatusRegistration
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rt, err := r.getRegistrationToken(ctx, repo)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return reg, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	expiresAt, err := time.Parse(time.RFC3339, rt.ExpiresAt)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return reg, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	reg.Repository = repo
 | 
					 | 
				
			||||||
	reg.Token = rt.Token
 | 
					 | 
				
			||||||
	reg.ExpiresAt = metav1.NewTime(expiresAt)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return reg, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (r *RunnerReconciler) getRegistrationToken(ctx context.Context, repo string) (GitHubRegistrationToken, error) {
 | 
					 | 
				
			||||||
	var regToken GitHubRegistrationToken
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	req, err := r.GitHubClient.NewRequest("POST", fmt.Sprintf("/repos/%s/actions/runners/registration-token", repo), nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return regToken, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	res, err := r.GitHubClient.Do(ctx, req, ®Token)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return regToken, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if res.StatusCode != 201 {
 | 
					 | 
				
			||||||
		return regToken, fmt.Errorf("unexpected status: %d", res.StatusCode)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return regToken, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (r *RunnerReconciler) unregisterRunner(ctx context.Context, repo, name string) (bool, error) {
 | 
					func (r *RunnerReconciler) unregisterRunner(ctx context.Context, repo, name string) (bool, error) {
 | 
				
			||||||
	runners, err := r.listRunners(ctx, repo)
 | 
						runners, err := r.GitHubClient.ListRunners(ctx, repo)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return false, err
 | 
							return false, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	id := 0
 | 
						id := int64(0)
 | 
				
			||||||
	for _, runner := range runners.Runners {
 | 
						for _, runner := range runners {
 | 
				
			||||||
		if runner.Name == name {
 | 
							if runner.GetName() == name {
 | 
				
			||||||
			id = runner.ID
 | 
								id = runner.GetID()
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if id == 0 {
 | 
						if id == int64(0) {
 | 
				
			||||||
		return false, nil
 | 
							return false, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := r.removeRunner(ctx, repo, id); err != nil {
 | 
						if err := r.GitHubClient.RemoveRunner(ctx, repo, id); err != nil {
 | 
				
			||||||
		return false, err
 | 
							return false, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *RunnerReconciler) listRunners(ctx context.Context, repo string) (GitHubRunnerList, error) {
 | 
					 | 
				
			||||||
	runners := GitHubRunnerList{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	req, err := r.GitHubClient.NewRequest("GET", fmt.Sprintf("/repos/%s/actions/runners", repo), nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return runners, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	res, err := r.GitHubClient.Do(ctx, req, &runners)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return runners, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if res.StatusCode != 200 {
 | 
					 | 
				
			||||||
		return runners, fmt.Errorf("unexpected status: %d", res.StatusCode)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return runners, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (r *RunnerReconciler) removeRunner(ctx context.Context, repo string, id int) error {
 | 
					 | 
				
			||||||
	req, err := r.GitHubClient.NewRequest("DELETE", fmt.Sprintf("/repos/%s/actions/runners/%d", repo, id), nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	res, err := r.GitHubClient.Do(ctx, req, nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if res.StatusCode != 204 {
 | 
					 | 
				
			||||||
		return fmt.Errorf("unexpected status: %d", res.StatusCode)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (r *RunnerReconciler) newPod(runner v1alpha1.Runner) (corev1.Pod, error) {
 | 
					func (r *RunnerReconciler) newPod(runner v1alpha1.Runner) (corev1.Pod, error) {
 | 
				
			||||||
	var (
 | 
						var (
 | 
				
			||||||
		privileged bool  = true
 | 
							privileged bool  = true
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,86 @@
 | 
				
			||||||
 | 
					package fake
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/http/httptest"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						RegistrationToken = "fake-registration-token"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RunnersListBody = `
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "total_count": 2,
 | 
				
			||||||
 | 
					  "runners": [
 | 
				
			||||||
 | 
					    {"id": 1, "name": "test1", "os": "linux", "status": "online"},
 | 
				
			||||||
 | 
					    {"id": 2, "name": "test2", "os": "linux", "status": "offline"}
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type handler struct {
 | 
				
			||||||
 | 
						Status int
 | 
				
			||||||
 | 
						Body   string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
 | 
						w.WriteHeader(h.Status)
 | 
				
			||||||
 | 
						fmt.Fprintf(w, h.Body)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewServer() *httptest.Server {
 | 
				
			||||||
 | 
						routes := map[string]handler{
 | 
				
			||||||
 | 
							// For CreateRegistrationToken
 | 
				
			||||||
 | 
							"/repos/test/valid/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)),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"/repos/test/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)),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"/repos/test/error/actions/runners/registration-token": handler{
 | 
				
			||||||
 | 
								Status: http.StatusBadRequest,
 | 
				
			||||||
 | 
								Body:   "",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// For ListRunners
 | 
				
			||||||
 | 
							"/repos/test/valid/actions/runners": handler{
 | 
				
			||||||
 | 
								Status: http.StatusOK,
 | 
				
			||||||
 | 
								Body:   RunnersListBody,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"/repos/test/invalid/actions/runners": handler{
 | 
				
			||||||
 | 
								Status: http.StatusNoContent,
 | 
				
			||||||
 | 
								Body:   "",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"/repos/test/error/actions/runners": handler{
 | 
				
			||||||
 | 
								Status: http.StatusBadRequest,
 | 
				
			||||||
 | 
								Body:   "",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// For RemoveRunner
 | 
				
			||||||
 | 
							"/repos/test/valid/actions/runners/1": handler{
 | 
				
			||||||
 | 
								Status: http.StatusNoContent,
 | 
				
			||||||
 | 
								Body:   "",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"/repos/test/invalid/actions/runners/1": handler{
 | 
				
			||||||
 | 
								Status: http.StatusOK,
 | 
				
			||||||
 | 
								Body:   "",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"/repos/test/error/actions/runners/1": handler{
 | 
				
			||||||
 | 
								Status: http.StatusBadRequest,
 | 
				
			||||||
 | 
								Body:   "",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mux := http.NewServeMux()
 | 
				
			||||||
 | 
						for path, handler := range routes {
 | 
				
			||||||
 | 
							h := handler
 | 
				
			||||||
 | 
							mux.Handle(path, &h)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return httptest.NewServer(mux)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,147 @@
 | 
				
			||||||
 | 
					package github
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/bradleyfalzon/ghinstallation"
 | 
				
			||||||
 | 
						"github.com/google/go-github/v31/github"
 | 
				
			||||||
 | 
						"golang.org/x/oauth2"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Client struct {
 | 
				
			||||||
 | 
						*github.Client
 | 
				
			||||||
 | 
						regTokens map[string]*github.RegistrationToken
 | 
				
			||||||
 | 
						mu        sync.Mutex
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewClient returns a client authenticated as a GitHub App.
 | 
				
			||||||
 | 
					func NewClient(appID, installationID int64, privateKeyPath string) (*Client, error) {
 | 
				
			||||||
 | 
						tr, err := ghinstallation.NewKeyFromFile(http.DefaultTransport, appID, installationID, privateKeyPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("authentication failed: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &Client{
 | 
				
			||||||
 | 
							Client:    github.NewClient(&http.Client{Transport: tr}),
 | 
				
			||||||
 | 
							regTokens: map[string]*github.RegistrationToken{},
 | 
				
			||||||
 | 
							mu:        sync.Mutex{},
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewClient returns a client authenticated with personal access token.
 | 
				
			||||||
 | 
					func NewClientWithAccessToken(token string) (*Client, error) {
 | 
				
			||||||
 | 
						tc := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(
 | 
				
			||||||
 | 
							&oauth2.Token{AccessToken: token},
 | 
				
			||||||
 | 
						))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &Client{
 | 
				
			||||||
 | 
							Client:    github.NewClient(tc),
 | 
				
			||||||
 | 
							regTokens: map[string]*github.RegistrationToken{},
 | 
				
			||||||
 | 
							mu:        sync.Mutex{},
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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) {
 | 
				
			||||||
 | 
						c.mu.Lock()
 | 
				
			||||||
 | 
						defer c.mu.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						owner, repo, err := splitOwnerAndRepo(repository)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						key := fmt.Sprintf("%s/%s", repo, name)
 | 
				
			||||||
 | 
						rt, ok := c.regTokens[key]
 | 
				
			||||||
 | 
						if ok && rt.GetExpiresAt().After(time.Now().Add(-10*time.Minute)) {
 | 
				
			||||||
 | 
							return rt, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rt, res, err := c.Client.Actions.CreateRegistrationToken(ctx, owner, repo)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to create registration token: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if res.StatusCode != 201 {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("unexpected status: %d", res.StatusCode)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c.regTokens[key] = rt
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							c.cleanup()
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rt, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RemoveRunner removes a runner with specified runner ID from repocitory.
 | 
				
			||||||
 | 
					func (c *Client) RemoveRunner(ctx context.Context, repository string, runnerID int64) error {
 | 
				
			||||||
 | 
						owner, repo, err := splitOwnerAndRepo(repository)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res, err := c.Client.Actions.RemoveRunner(ctx, owner, repo, runnerID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to remove runner: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if res.StatusCode != 204 {
 | 
				
			||||||
 | 
							return fmt.Errorf("unexpected status: %d", res.StatusCode)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListRunners returns a list of runners of specified repository name.
 | 
				
			||||||
 | 
					func (c *Client) ListRunners(ctx context.Context, repository string) ([]*github.Runner, error) {
 | 
				
			||||||
 | 
						var runners []*github.Runner
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						owner, repo, err := splitOwnerAndRepo(repository)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return runners, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := github.ListOptions{PerPage: 10}
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							list, res, err := c.Client.Actions.ListRunners(ctx, owner, repo, &opts)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return runners, fmt.Errorf("failed to remove runner: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							runners = append(runners, list.Runners...)
 | 
				
			||||||
 | 
							if res.NextPage == 0 {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							opts.Page = res.NextPage
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return runners, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// cleanup removes expired registration tokens.
 | 
				
			||||||
 | 
					func (c *Client) cleanup() {
 | 
				
			||||||
 | 
						c.mu.Lock()
 | 
				
			||||||
 | 
						defer c.mu.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for key, rt := range c.regTokens {
 | 
				
			||||||
 | 
							if rt.GetExpiresAt().Before(time.Now()) {
 | 
				
			||||||
 | 
								delete(c.regTokens, key)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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,124 @@
 | 
				
			||||||
 | 
					package github
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"net/http/httptest"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/google/go-github/v31/github"
 | 
				
			||||||
 | 
						"github.com/summerwind/actions-runner-controller/github/fake"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var server *httptest.Server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newTestClient() *Client {
 | 
				
			||||||
 | 
						client, err := NewClientWithAccessToken("token")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						baseURL, err := url.Parse(server.URL + "/")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						client.Client.BaseURL = baseURL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return client
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestMain(m *testing.M) {
 | 
				
			||||||
 | 
						server = fake.NewServer()
 | 
				
			||||||
 | 
						defer server.Close()
 | 
				
			||||||
 | 
						m.Run()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGetRegistrationToken(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							repo  string
 | 
				
			||||||
 | 
							token string
 | 
				
			||||||
 | 
							err   bool
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{repo: "test/valid", token: fake.RegistrationToken, err: false},
 | 
				
			||||||
 | 
							{repo: "test/invalid", token: "", err: true},
 | 
				
			||||||
 | 
							{repo: "test/error", token: "", err: true},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client := newTestClient()
 | 
				
			||||||
 | 
						for i, tt := range tests {
 | 
				
			||||||
 | 
							rt, err := client.GetRegistrationToken(context.Background(), tt.repo, "test")
 | 
				
			||||||
 | 
							if !tt.err && err != nil {
 | 
				
			||||||
 | 
								t.Errorf("[%d] unexpected error: %v", i, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if tt.token != rt.GetToken() {
 | 
				
			||||||
 | 
								t.Errorf("[%d] unexpected token: %v", i, rt.GetToken())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestListRunners(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							repo   string
 | 
				
			||||||
 | 
							length int
 | 
				
			||||||
 | 
							err    bool
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{repo: "test/valid", length: 2, err: false},
 | 
				
			||||||
 | 
							{repo: "test/invalid", length: 0, err: true},
 | 
				
			||||||
 | 
							{repo: "test/error", length: 0, err: true},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client := newTestClient()
 | 
				
			||||||
 | 
						for i, tt := range tests {
 | 
				
			||||||
 | 
							runners, err := client.ListRunners(context.Background(), tt.repo)
 | 
				
			||||||
 | 
							if !tt.err && err != nil {
 | 
				
			||||||
 | 
								t.Errorf("[%d] unexpected error: %v", i, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if tt.length != len(runners) {
 | 
				
			||||||
 | 
								t.Errorf("[%d] unexpected runners list: %v", i, runners)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRemoveRunner(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							repo string
 | 
				
			||||||
 | 
							err  bool
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{repo: "test/valid", err: false},
 | 
				
			||||||
 | 
							{repo: "test/invalid", err: true},
 | 
				
			||||||
 | 
							{repo: "test/error", err: true},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client := newTestClient()
 | 
				
			||||||
 | 
						for i, tt := range tests {
 | 
				
			||||||
 | 
							err := client.RemoveRunner(context.Background(), tt.repo, int64(1))
 | 
				
			||||||
 | 
							if !tt.err && err != nil {
 | 
				
			||||||
 | 
								t.Errorf("[%d] unexpected error: %v", i, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCleanup(t *testing.T) {
 | 
				
			||||||
 | 
						token := "token"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client := newTestClient()
 | 
				
			||||||
 | 
						client.regTokens = map[string]*github.RegistrationToken{
 | 
				
			||||||
 | 
							"active": &github.RegistrationToken{
 | 
				
			||||||
 | 
								Token:     &token,
 | 
				
			||||||
 | 
								ExpiresAt: &github.Timestamp{Time: time.Now().Add(time.Hour * 1)},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"expired": &github.RegistrationToken{
 | 
				
			||||||
 | 
								Token:     &token,
 | 
				
			||||||
 | 
								ExpiresAt: &github.Timestamp{Time: time.Now().Add(-time.Hour * 1)},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client.cleanup()
 | 
				
			||||||
 | 
						if _, ok := client.regTokens["active"]; !ok {
 | 
				
			||||||
 | 
							t.Errorf("active token was accidentally removed")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if _, ok := client.regTokens["expired"]; ok {
 | 
				
			||||||
 | 
							t.Errorf("expired token still exists")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										8
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										8
									
								
								go.mod
								
								
								
								
							| 
						 | 
					@ -3,18 +3,14 @@ module github.com/summerwind/actions-runner-controller
 | 
				
			||||||
go 1.13
 | 
					go 1.13
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
 | 
					 | 
				
			||||||
	github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
 | 
					 | 
				
			||||||
	github.com/bradleyfalzon/ghinstallation v1.1.1
 | 
						github.com/bradleyfalzon/ghinstallation v1.1.1
 | 
				
			||||||
	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 v17.0.0+incompatible
 | 
						github.com/google/go-github/v31 v31.0.0
 | 
				
			||||||
	github.com/google/go-github/v29 v29.0.3
 | 
					 | 
				
			||||||
	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/prometheus/common v0.0.0-20181126121408-4724e9255275
 | 
						github.com/stretchr/testify v1.4.0 // indirect
 | 
				
			||||||
	golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
 | 
						golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
 | 
				
			||||||
	gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect
 | 
					 | 
				
			||||||
	k8s.io/api v0.0.0-20190918155943-95b840bb6a1f
 | 
						k8s.io/api v0.0.0-20190918155943-95b840bb6a1f
 | 
				
			||||||
	k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655
 | 
						k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655
 | 
				
			||||||
	k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90
 | 
						k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										13
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										13
									
								
								go.sum
								
								
								
								
							| 
						 | 
					@ -18,10 +18,6 @@ github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
 | 
				
			||||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 | 
					github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 | 
				
			||||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 | 
					github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 | 
				
			||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 | 
					github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 | 
				
			||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
 | 
					 | 
				
			||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 | 
					 | 
				
			||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
 | 
					 | 
				
			||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
 | 
					 | 
				
			||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 | 
					github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 | 
				
			||||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 | 
					github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 | 
				
			||||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 | 
					github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 | 
				
			||||||
| 
						 | 
					@ -120,12 +116,10 @@ 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 v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
 | 
					 | 
				
			||||||
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/v29 v29.0.3 h1:IktKCTwU//aFHnpA+2SLIi7Oo9uhAzgsdZNbcAqhgdc=
 | 
					github.com/google/go-github/v31 v31.0.0 h1:JJUxlP9lFK+ziXKimTCprajMApV1ecWD4NB6CCb0plo=
 | 
				
			||||||
github.com/google/go-github/v29 v29.0.3/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E=
 | 
					github.com/google/go-github/v31 v31.0.0/go.mod h1:NQPZol8/1sMoWYGN2yaALIBytu17gAWfhbweiEed3pM=
 | 
				
			||||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
 | 
					github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
 | 
				
			||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 | 
					github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 | 
				
			||||||
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
 | 
					github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
 | 
				
			||||||
| 
						 | 
					@ -237,6 +231,7 @@ github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRci
 | 
				
			||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
					github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
				
			||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 | 
					github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 | 
				
			||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
					github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 | 
				
			||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
					github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
				
			||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 | 
					github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 | 
				
			||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 | 
					github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 | 
				
			||||||
| 
						 | 
					@ -343,8 +338,6 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn
 | 
				
			||||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
					google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
				
			||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
					google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
				
			||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 | 
					google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 | 
				
			||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
 | 
					 | 
				
			||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 | 
					 | 
				
			||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 | 
					gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										25
									
								
								main.go
								
								
								
								
							
							
						
						
									
										25
									
								
								main.go
								
								
								
								
							| 
						 | 
					@ -17,18 +17,14 @@ limitations under the License.
 | 
				
			||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
					 | 
				
			||||||
	"flag"
 | 
						"flag"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
					 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/bradleyfalzon/ghinstallation"
 | 
					 | 
				
			||||||
	"github.com/google/go-github/v29/github"
 | 
					 | 
				
			||||||
	actionsv1alpha1 "github.com/summerwind/actions-runner-controller/api/v1alpha1"
 | 
						actionsv1alpha1 "github.com/summerwind/actions-runner-controller/api/v1alpha1"
 | 
				
			||||||
	"github.com/summerwind/actions-runner-controller/controllers"
 | 
						"github.com/summerwind/actions-runner-controller/controllers"
 | 
				
			||||||
	"golang.org/x/oauth2"
 | 
						"github.com/summerwind/actions-runner-controller/github"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
						"k8s.io/apimachinery/pkg/runtime"
 | 
				
			||||||
	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
 | 
						clientgoscheme "k8s.io/client-go/kubernetes/scheme"
 | 
				
			||||||
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
 | 
						_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
 | 
				
			||||||
| 
						 | 
					@ -56,6 +52,9 @@ func init() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
	var (
 | 
						var (
 | 
				
			||||||
 | 
							err      error
 | 
				
			||||||
 | 
							ghClient *github.Client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		metricsAddr          string
 | 
							metricsAddr          string
 | 
				
			||||||
		enableLeaderElection bool
 | 
							enableLeaderElection bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,8 +65,6 @@ func main() {
 | 
				
			||||||
		ghAppID             int64
 | 
							ghAppID             int64
 | 
				
			||||||
		ghAppInstallationID int64
 | 
							ghAppInstallationID int64
 | 
				
			||||||
		ghAppPrivateKey     string
 | 
							ghAppPrivateKey     string
 | 
				
			||||||
 | 
					 | 
				
			||||||
		ghClient *github.Client
 | 
					 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
 | 
						flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
 | 
				
			||||||
| 
						 | 
					@ -111,17 +108,17 @@ func main() {
 | 
				
			||||||
			os.Exit(1)
 | 
								os.Exit(1)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		tr, err := ghinstallation.NewKeyFromFile(http.DefaultTransport, ghAppID, ghAppInstallationID, ghAppPrivateKey)
 | 
							ghClient, err = github.NewClient(ghAppID, ghAppInstallationID, ghAppPrivateKey)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			fmt.Fprintf(os.Stderr, "Error: Invalid GitHub App credentials: %v\n", err)
 | 
								fmt.Fprintf(os.Stderr, "Error: Failed to create GitHub client: %v\n", err)
 | 
				
			||||||
			os.Exit(1)
 | 
								os.Exit(1)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ghClient = github.NewClient(&http.Client{Transport: tr})
 | 
					 | 
				
			||||||
	} else if ghToken != "" {
 | 
						} else if ghToken != "" {
 | 
				
			||||||
		tc := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(
 | 
							ghClient, err = github.NewClientWithAccessToken(ghToken)
 | 
				
			||||||
			&oauth2.Token{AccessToken: ghToken},
 | 
							if err != nil {
 | 
				
			||||||
		))
 | 
								fmt.Fprintf(os.Stderr, "Error: Failed to create GitHub client: %v\n", err)
 | 
				
			||||||
		ghClient = github.NewClient(tc)
 | 
								os.Exit(1)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		fmt.Fprintln(os.Stderr, "Error: GitHub App credentials or personal access token must be specified.")
 | 
							fmt.Fprintln(os.Stderr, "Error: GitHub App credentials or personal access token must be specified.")
 | 
				
			||||||
		os.Exit(1)
 | 
							os.Exit(1)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue