Skip to content

Commit

Permalink
Add support for enterprise runners (#290)
Browse files Browse the repository at this point in the history
* Add support for enterprise runners

* update docs
  • Loading branch information
zetaab authored Feb 5, 2021
1 parent 831db9e commit 28e80a2
Show file tree
Hide file tree
Showing 16 changed files with 195 additions and 90 deletions.
44 changes: 42 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,53 @@ helm upgrade --install -n actions-runner-system actions-runner-controller/action

### Github Enterprise support

If you use either Github Enterprise Cloud or Server (and have recent enought version supporting Actions), you can use **actions-runner-controller** with those, too. Authentication works same way as with public Github (repo and organization level).
If you use either Github Enterprise Cloud or Server, you can use **actions-runner-controller** with those, too.
Authentication works same way as with public Github (repo and organization level).
The minimum version of Github Enterprise Server is 3.0.0 (or rc1/rc2).
In most cases maintainers do not have environment where to test changes and are reliant on the community for testing.


```shell
kubectl set env deploy controller-manager -c manager GITHUB_ENTERPRISE_URL=<GHEC/S URL> --namespace actions-runner-system
```

[Enterprise level](https://docs.github.com/en/enterprise-server@2.22/actions/hosting-your-own-runners/adding-self-hosted-runners#adding-a-self-hosted-runner-to-an-enterprise) runners are not working yet as there's no API definition for those.
#### Enterprise runners usage

In order to use enterprise runners you must have Admin access to Github Enterprise and you should do Personal Access Token (PAT)
with `enterprise:admin` access. Enterprise runners are not possible to run with Github APP or any other permission.

When you use enterprise runners those will get access to Github Organisations. However, access to the repositories is **NOT**
allowed by default. Each Github Organisation must allow Enterprise runner groups to be used in repositories.
This is needed only one time and is permanent after that.

Example:

```yaml
apiVersion: actions.summerwind.dev/v1alpha1
kind: RunnerDeployment
metadata:
name: ghe-runner-deployment
spec:
replicas: 2
template:
spec:
enterprise: your-enterprise-name
dockerdWithinRunnerContainer: true
resources:
limits:
cpu: "4000m"
memory: "2Gi"
requests:
cpu: "200m"
memory: "200Mi"
volumeMounts:
- mountPath: /runner
name: runner
volumes:
- name: runner
emptyDir: {}

```

## Setting up authentication with GitHub API

Expand Down
26 changes: 21 additions & 5 deletions api/v1alpha1/runner_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ import (

// RunnerSpec defines the desired state of Runner
type RunnerSpec struct {
// +optional
// +kubebuilder:validation:Pattern=`^[^/]+$`
Enterprise string `json:"enterprise,omitempty"`

// +optional
// +kubebuilder:validation:Pattern=`^[^/]+$`
Organization string `json:"organization,omitempty"`
Expand Down Expand Up @@ -92,12 +96,22 @@ type RunnerSpec struct {

// ValidateRepository validates repository field.
func (rs *RunnerSpec) ValidateRepository() error {
// Organization and repository are both exclusive.
if len(rs.Organization) == 0 && len(rs.Repository) == 0 {
return errors.New("Spec needs organization or repository")
// Enterprise, Organization and repository are both exclusive.
foundCount := 0
if len(rs.Organization) > 0 {
foundCount += 1
}
if len(rs.Repository) > 0 {
foundCount += 1
}
if len(rs.Enterprise) > 0 {
foundCount += 1
}
if foundCount == 0 {
return errors.New("Spec needs enterprise, organization or repository")
}
if len(rs.Organization) > 0 && len(rs.Repository) > 0 {
return errors.New("Spec cannot have both organization and repository")
if foundCount > 1 {
return errors.New("Spec cannot have many fields defined enterprise, organization and repository")
}

return nil
Expand All @@ -113,6 +127,7 @@ type RunnerStatus struct {

// RunnerStatusRegistration contains runner registration status
type RunnerStatusRegistration struct {
Enterprise string `json:"enterprise,omitempty"`
Organization string `json:"organization,omitempty"`
Repository string `json:"repository,omitempty"`
Labels []string `json:"labels,omitempty"`
Expand All @@ -122,6 +137,7 @@ type RunnerStatusRegistration struct {

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:JSONPath=".spec.enterprise",name=Enterprise,type=string
// +kubebuilder:printcolumn:JSONPath=".spec.organization",name=Organization,type=string
// +kubebuilder:printcolumn:JSONPath=".spec.repository",name=Repository,type=string
// +kubebuilder:printcolumn:JSONPath=".spec.labels",name=Labels,type=string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,9 @@ spec:
type: object
dockerdWithinRunnerContainer:
type: boolean
enterprise:
pattern: ^[^/]+$
type: string
env:
items:
description: EnvVar represents an environment variable present in a Container.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,9 @@ spec:
type: object
dockerdWithinRunnerContainer:
type: boolean
enterprise:
pattern: ^[^/]+$
type: string
env:
items:
description: EnvVar represents an environment variable present in a Container.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ metadata:
name: runners.actions.summerwind.dev
spec:
additionalPrinterColumns:
- JSONPath: .spec.enterprise
name: Enterprise
type: string
- JSONPath: .spec.organization
name: Organization
type: string
Expand Down Expand Up @@ -419,6 +422,9 @@ spec:
type: object
dockerdWithinRunnerContainer:
type: boolean
enterprise:
pattern: ^[^/]+$
type: string
env:
items:
description: EnvVar represents an environment variable present in a Container.
Expand Down Expand Up @@ -1541,6 +1547,8 @@ spec:
registration:
description: RunnerStatusRegistration contains runner registration status
properties:
enterprise:
type: string
expiresAt:
format: date-time
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,9 @@ spec:
type: object
dockerdWithinRunnerContainer:
type: boolean
enterprise:
pattern: ^[^/]+$
type: string
env:
items:
description: EnvVar represents an environment variable present in a Container.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,9 @@ spec:
type: object
dockerdWithinRunnerContainer:
type: boolean
enterprise:
pattern: ^[^/]+$
type: string
env:
items:
description: EnvVar represents an environment variable present in a Container.
Expand Down
8 changes: 8 additions & 0 deletions config/crd/bases/actions.summerwind.dev_runners.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ metadata:
name: runners.actions.summerwind.dev
spec:
additionalPrinterColumns:
- JSONPath: .spec.enterprise
name: Enterprise
type: string
- JSONPath: .spec.organization
name: Organization
type: string
Expand Down Expand Up @@ -419,6 +422,9 @@ spec:
type: object
dockerdWithinRunnerContainer:
type: boolean
enterprise:
pattern: ^[^/]+$
type: string
env:
items:
description: EnvVar represents an environment variable present in a Container.
Expand Down Expand Up @@ -1541,6 +1547,8 @@ spec:
registration:
description: RunnerStatusRegistration contains runner registration status
properties:
enterprise:
type: string
expiresAt:
format: date-time
type: string
Expand Down
2 changes: 1 addition & 1 deletion controllers/autoscaling.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ func (r *HorizontalRunnerAutoscalerReconciler) calculateReplicasByPercentageRunn
}

// ListRunners will return all runners managed by GitHub - not restricted to ns
runners, err := r.GitHubClient.ListRunners(ctx, orgName, "")
runners, err := r.GitHubClient.ListRunners(ctx, "", orgName, "")
if err != nil {
return nil, err
}
Expand Down
20 changes: 12 additions & 8 deletions controllers/runner_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func (r *RunnerReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {

if removed {
if len(runner.Status.Registration.Token) > 0 {
ok, err := r.unregisterRunner(ctx, runner.Spec.Organization, runner.Spec.Repository, runner.Name)
ok, err := r.unregisterRunner(ctx, runner.Spec.Enterprise, runner.Spec.Organization, runner.Spec.Repository, runner.Name)
if err != nil {
log.Error(err, "Failed to unregister runner")
return ctrl.Result{}, err
Expand Down Expand Up @@ -194,7 +194,7 @@ func (r *RunnerReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
return ctrl.Result{}, err
}

runnerBusy, err := r.isRunnerBusy(ctx, runner.Spec.Organization, runner.Spec.Repository, runner.Name)
runnerBusy, err := r.isRunnerBusy(ctx, runner.Spec.Enterprise, runner.Spec.Organization, runner.Spec.Repository, runner.Name)
if err != nil {
log.Error(err, "Failed to check if runner is busy")
return ctrl.Result{}, nil
Expand Down Expand Up @@ -227,8 +227,8 @@ func (r *RunnerReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
return ctrl.Result{}, nil
}

func (r *RunnerReconciler) isRunnerBusy(ctx context.Context, org, repo, name string) (bool, error) {
runners, err := r.GitHubClient.ListRunners(ctx, org, repo)
func (r *RunnerReconciler) isRunnerBusy(ctx context.Context, enterprise, org, repo, name string) (bool, error) {
runners, err := r.GitHubClient.ListRunners(ctx, enterprise, org, repo)
if err != nil {
return false, err
}
Expand All @@ -242,8 +242,8 @@ func (r *RunnerReconciler) isRunnerBusy(ctx context.Context, org, repo, name str
return false, fmt.Errorf("runner not found")
}

func (r *RunnerReconciler) unregisterRunner(ctx context.Context, org, repo, name string) (bool, error) {
runners, err := r.GitHubClient.ListRunners(ctx, org, repo)
func (r *RunnerReconciler) unregisterRunner(ctx context.Context, enterprise, org, repo, name string) (bool, error) {
runners, err := r.GitHubClient.ListRunners(ctx, enterprise, org, repo)
if err != nil {
return false, err
}
Expand All @@ -263,7 +263,7 @@ func (r *RunnerReconciler) unregisterRunner(ctx context.Context, org, repo, name
return false, nil
}

if err := r.GitHubClient.RemoveRunner(ctx, org, repo, id); err != nil {
if err := r.GitHubClient.RemoveRunner(ctx, enterprise, org, repo, id); err != nil {
return false, err
}

Expand All @@ -277,7 +277,7 @@ func (r *RunnerReconciler) updateRegistrationToken(ctx context.Context, runner v

log := r.Log.WithValues("runner", runner.Name)

rt, err := r.GitHubClient.GetRegistrationToken(ctx, runner.Spec.Organization, runner.Spec.Repository, runner.Name)
rt, err := r.GitHubClient.GetRegistrationToken(ctx, runner.Spec.Enterprise, runner.Spec.Organization, runner.Spec.Repository, runner.Name)
if err != nil {
r.Recorder.Event(&runner, corev1.EventTypeWarning, "FailedUpdateRegistrationToken", "Updating registration token failed")
log.Error(err, "Failed to get new registration token")
Expand Down Expand Up @@ -339,6 +339,10 @@ func (r *RunnerReconciler) newPod(runner v1alpha1.Runner) (corev1.Pod, error) {
Name: "RUNNER_REPO",
Value: runner.Spec.Repository,
},
{
Name: "RUNNER_ENTERPRISE",
Value: runner.Spec.Enterprise,
},
{
Name: "RUNNER_LABELS",
Value: strings.Join(runner.Spec.Labels, ","),
Expand Down
6 changes: 3 additions & 3 deletions controllers/runnerreplicaset_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func (r *RunnerReplicaSetReconciler) Reconcile(req ctrl.Request) (ctrl.Result, e
// get runners that are currently not busy
var notBusy []v1alpha1.Runner
for _, runner := range myRunners {
busy, err := r.isRunnerBusy(ctx, runner.Spec.Organization, runner.Spec.Repository, runner.Name)
busy, err := r.isRunnerBusy(ctx, runner.Spec.Enterprise, runner.Spec.Organization, runner.Spec.Repository, runner.Name)
if err != nil {
log.Error(err, "Failed to check if runner is busy")
return ctrl.Result{}, err
Expand Down Expand Up @@ -187,8 +187,8 @@ func (r *RunnerReplicaSetReconciler) SetupWithManager(mgr ctrl.Manager) error {
Complete(r)
}

func (r *RunnerReplicaSetReconciler) isRunnerBusy(ctx context.Context, org, repo, name string) (bool, error) {
runners, err := r.GitHubClient.ListRunners(ctx, org, repo)
func (r *RunnerReplicaSetReconciler) isRunnerBusy(ctx context.Context, enterprise, org, repo, name string) (bool, error) {
runners, err := r.GitHubClient.ListRunners(ctx, enterprise, org, repo)
r.Log.Info("runners", "github", runners)
if err != nil {
return false, err
Expand Down
Loading

0 comments on commit 28e80a2

Please sign in to comment.