Get outbound IP in multiple ways or disable cache server if failed to init (#74)

Fix #64 (incompletely).

It's still not ideal. It makes more sense to use the gateway IP address of container network as outbound IP of cache server. However, this requires act to cooperate, some think like:

- act creates the network for new container, and returns the network to runner.
- runner extracts the gateway IP in the network.
- runner uses the gateway IP as outbound IP, and pass it to act as cache server endpoint.
- act It continues to create the container with the created network.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/74
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
Jason Song 2023-03-24 17:55:13 +08:00
parent bc1842d649
commit c9e076db68
4 changed files with 48 additions and 11 deletions

View File

@ -18,7 +18,6 @@ import (
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware" "github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/render" "github.com/go-chi/render"
"github.com/nektos/act/pkg/common"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
_ "modernc.org/sqlite" _ "modernc.org/sqlite"
"xorm.io/builder" "xorm.io/builder"
@ -39,6 +38,8 @@ type Handler struct {
gc atomic.Bool gc atomic.Bool
gcAt time.Time gcAt time.Time
outboundIP string
} }
func NewHandler() (*Handler, error) { func NewHandler() (*Handler, error) {
@ -69,6 +70,12 @@ func NewHandler() (*Handler, error) {
} }
h.storage = storage h.storage = storage
if ip, err := getOutboundIP(); err != nil {
return nil, err
} else {
h.outboundIP = ip.String()
}
router := chi.NewRouter() router := chi.NewRouter()
router.Use(middleware.RequestLogger(&middleware.DefaultLogFormatter{Logger: logger})) router.Use(middleware.RequestLogger(&middleware.DefaultLogFormatter{Logger: logger}))
router.Use(func(handler http.Handler) http.Handler { router.Use(func(handler http.Handler) http.Handler {
@ -113,7 +120,7 @@ func NewHandler() (*Handler, error) {
func (h *Handler) ExternalURL() string { func (h *Handler) ExternalURL() string {
// TODO: make the external url configurable if necessary // TODO: make the external url configurable if necessary
return fmt.Sprintf("http://%s:%d", return fmt.Sprintf("http://%s:%d",
common.GetOutboundIP().String(), h.outboundIP,
h.listener.Addr().(*net.TCPAddr).Port) h.listener.Addr().(*net.TCPAddr).Port)
} }

View File

@ -5,6 +5,7 @@ package artifactcache
import ( import (
"fmt" "fmt"
"net"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@ -44,8 +45,35 @@ func parseContentRange(s string) (int64, int64, error) {
return start, stop, nil return start, stop, nil
} }
func getOutboundIP() (net.IP, error) {
// FIXME: It makes more sense to use the gateway IP address of container network
if conn, err := net.Dial("udp", "8.8.8.8:80"); err == nil {
defer conn.Close()
return conn.LocalAddr().(*net.UDPAddr).IP, nil
}
if ifaces, err := net.Interfaces(); err == nil {
for _, i := range ifaces {
if addrs, err := i.Addrs(); err == nil {
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
if ip.IsGlobalUnicast() {
return ip, nil
}
}
}
}
}
return nil, fmt.Errorf("no outbound IP address found")
}
// engine is a wrapper of *xorm.Engine, with a lock. // engine is a wrapper of *xorm.Engine, with a lock.
// To avoid racing of sqlite, we don't careperformance here. // To avoid racing of sqlite, we don't care performance here.
type engine struct { type engine struct {
e *xorm.Engine e *xorm.Engine
m sync.Mutex m sync.Mutex

View File

@ -52,12 +52,6 @@ func runDaemon(ctx context.Context, envFile string) func(cmd *cobra.Command, arg
} }
} }
handler, err := artifactcache.NewHandler()
if err != nil {
return err
}
log.Infof("cache handler listens on: %v", handler.ExternalURL())
var g errgroup.Group var g errgroup.Group
cli := client.New( cli := client.New(
@ -75,7 +69,13 @@ func runDaemon(ctx context.Context, envFile string) func(cmd *cobra.Command, arg
Environ: cfg.Runner.Environ, Environ: cfg.Runner.Environ,
Labels: cfg.Runner.Labels, Labels: cfg.Runner.Labels,
Version: version, Version: version,
CacheHandler: handler, }
if handler, err := artifactcache.NewHandler(); err != nil {
log.Errorf("cannot init cache server, it will be disabled: %v", err)
} else {
log.Infof("cache handler listens on: %v", handler.ExternalURL())
runner.CacheHandler = handler
} }
poller := poller.New( poller := poller.New(

View File

@ -31,7 +31,9 @@ func (s *Runner) Run(ctx context.Context, task *runnerv1.Task) error {
for k, v := range s.Environ { for k, v := range s.Environ {
env[k] = v env[k] = v
} }
env["ACTIONS_CACHE_URL"] = s.CacheHandler.ExternalURL() + "/" if s.CacheHandler != nil {
env["ACTIONS_CACHE_URL"] = s.CacheHandler.ExternalURL() + "/"
}
return NewTask(s.ForgeInstance, task.Id, s.Client, env, s.platformPicker).Run(ctx, task, s.Machine, s.Version) return NewTask(s.ForgeInstance, task.Id, s.Client, env, s.platformPicker).Run(ctx, task, s.Machine, s.Version)
} }