From 990cf93c7136669408eb1832cd05df3ad4dd81b3 Mon Sep 17 00:00:00 2001
From: ChristopherHX <christopherhx@noreply.gitea.io>
Date: Fri, 27 Jan 2023 20:42:02 +0800
Subject: [PATCH] feat: don't require docker (#16)

The only reason docker is really required by now, is that act_runner ping docker.
This change only pings docker if a label with `docker://` is added to the runner.

Plain labels without `:` like `self-hosted` are run directly on the system. Previously the pseudo non docker label `-self-hosted` have been required like this `self-hosted:docker://-self-hosted`, but due to docker ping this still required a dockerd to be pingable.

Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/16
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: ChristopherHX <christopherhx@noreply.gitea.io>
Co-committed-by: ChristopherHX <christopherhx@noreply.gitea.io>
---
 README.md            |  2 +-
 cmd/daemon.go        | 21 +++++++++++++++++----
 cmd/register.go      |  5 +++--
 cmd/register_test.go |  2 +-
 runtime/runtime.go   |  6 ++++++
 5 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/README.md b/README.md
index 0e121dd..b5cf959 100644
--- a/README.md
+++ b/README.md
@@ -39,7 +39,7 @@ INFO Enter the runner token:
 fe884e8027dc292970d4e0303fe82b14xxxxxxxx
 INFO Enter the runner name (if set empty, use hostname:Test.local ):
 
-INFO Enter the runner labels, leave blank to use the default labels (comma-separated, for example, ubuntu-20.04:docker://node:16-bullseye,ubuntu-18.04:docker://node:16-buster):
+INFO Enter the runner labels, leave blank to use the default labels (comma-separated, for example, self-hosted,ubuntu-20.04:docker://node:16-bullseye,ubuntu-18.04:docker://node:16-buster):
 
 INFO Registering runner, name=Test.local, instance=http://192.168.8.8:3000/, labels=[ubuntu-latest:docker://node:16-bullseye ubuntu-22.04:docker://node:16-bullseye ubuntu-20.04:docker://node:16-bullseye ubuntu-18.04:docker://node:16-buster].
 DEBU Successfully pinged the Gitea instance server
diff --git a/cmd/daemon.go b/cmd/daemon.go
index c171363..fe0c40e 100644
--- a/cmd/daemon.go
+++ b/cmd/daemon.go
@@ -3,6 +3,7 @@ package cmd
 import (
 	"context"
 	"os"
+	"strings"
 
 	"gitea.com/gitea/act_runner/client"
 	"gitea.com/gitea/act_runner/config"
@@ -30,10 +31,22 @@ func runDaemon(ctx context.Context, envFile string) func(cmd *cobra.Command, arg
 
 		initLogging(cfg)
 
-		// try to connect to docker daemon
-		// if failed, exit with error
-		if err := engine.Start(ctx); err != nil {
-			log.WithError(err).Fatalln("failed to connect docker daemon engine")
+		// require docker if a runner label uses a docker backend
+		needsDocker := false
+		for _, l := range cfg.Runner.Labels {
+			splits := strings.SplitN(l, ":", 2)
+			if len(splits) == 2 && strings.HasPrefix(splits[1], "docker://") {
+				needsDocker = true
+				break
+			}
+		}
+
+		if needsDocker {
+			// try to connect to docker daemon
+			// if failed, exit with error
+			if err := engine.Start(ctx); err != nil {
+				log.WithError(err).Fatalln("failed to connect docker daemon engine")
+			}
 		}
 
 		var g errgroup.Group
diff --git a/cmd/register.go b/cmd/register.go
index 839ad3e..56d8a0f 100644
--- a/cmd/register.go
+++ b/cmd/register.go
@@ -119,9 +119,10 @@ func (r *registerInputs) validate() error {
 func validateLabels(labels []string) error {
 	for _, label := range labels {
 		values := strings.SplitN(label, ":", 2)
-		if len(values) != 2 {
+		if len(values) > 2 {
 			return fmt.Errorf("Invalid label: %s", label)
 		}
+		// len(values) == 1, label for non docker execution environment
 		// TODO: validate value format, like docker://node:16-buster
 	}
 	return nil
@@ -227,7 +228,7 @@ func printStageHelp(stage registerStage) {
 		hostname, _ := os.Hostname()
 		log.Infof("Enter the runner name (if set empty, use hostname:%s ):\n", hostname)
 	case StageInputCustomLabels:
-		log.Infoln("Enter the runner labels, leave blank to use the default labels (comma-separated, for example, ubuntu-20.04:docker://node:16-bullseye,ubuntu-18.04:docker://node:16-buster):")
+		log.Infoln("Enter the runner labels, leave blank to use the default labels (comma-separated, for example, self-hosted,ubuntu-20.04:docker://node:16-bullseye,ubuntu-18.04:docker://node:16-buster):")
 	case StageWaitingForRegistration:
 		log.Infoln("Waiting for registration...")
 	}
diff --git a/cmd/register_test.go b/cmd/register_test.go
index b00a10a..0179a97 100644
--- a/cmd/register_test.go
+++ b/cmd/register_test.go
@@ -3,7 +3,7 @@ package cmd
 import "testing"
 
 func TestValidateLabels(t *testing.T) {
-	labels := []string{"ubuntu-latest:docker://node:16-buster"}
+	labels := []string{"ubuntu-latest:docker://node:16-buster", "self-hosted"}
 	if err := validateLabels(labels); err != nil {
 		t.Errorf("validateLabels() error = %v", err)
 	}
diff --git a/runtime/runtime.go b/runtime/runtime.go
index 0fe3a14..4bac678 100644
--- a/runtime/runtime.go
+++ b/runtime/runtime.go
@@ -24,11 +24,17 @@ func (s *Runner) Run(ctx context.Context, task *runnerv1.Task) error {
 
 func (s *Runner) platformPicker(labels []string) string {
 	// "ubuntu-18.04:docker://node:16-buster"
+	// "self-hosted"
 
 	platforms := make(map[string]string, len(labels))
 	for _, l := range s.Labels {
 		// "ubuntu-18.04:docker://node:16-buster"
 		splits := strings.SplitN(l, ":", 2)
+		if len(splits) == 1 {
+			// identifier for non docker execution environment
+			platforms[splits[0]] = "-self-hosted"
+			continue
+		}
 		// ["ubuntu-18.04", "docker://node:16-buster"]
 		k, v := splits[0], splits[1]