mirror of
https://gitea.com/gitea/runner.git
synced 2026-11-04 06:25:32 +00:00
refactor(logging): use the logging package in internal (#1302)
Move the log calls in `internal` onto `logging`, and add what the process-wide logger setup and its tests need: `SetTextFormat` with a line filter for secret masking (replacing `report.MaskingFormatter`), `SetReportCaller`, `SetDefault` and `logtest.SetDefault`. After this only the `logging` package imports logrus. Stacked on https://gitea.com/gitea/runner/pulls/1301. Assisted-by: Codet Co-authored-by: bircni <[email protected]> Reviewed-on: https://gitea.com/gitea/runner/pulls/1302 Reviewed-by: bircni <[email protected]>
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package logging
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// TextOptions describes how the process-wide Logger writes a record as text.
|
||||
type TextOptions struct {
|
||||
DisableColors bool
|
||||
DisableTimestamp bool
|
||||
FullTimestamp bool
|
||||
// CallerPrettyfier renames the function and file reported with a record when ReportCaller is on.
|
||||
CallerPrettyfier func(*runtime.Frame) (function, file string)
|
||||
// Filter rewrites each formatted line, for example to mask secrets.
|
||||
Filter func(line []byte) []byte
|
||||
}
|
||||
|
||||
// SetTextFormat sets the text format of the process-wide Logger.
|
||||
func SetTextFormat(o TextOptions) {
|
||||
logrus.SetFormatter(&filteringFormatter{
|
||||
inner: &logrus.TextFormatter{
|
||||
DisableColors: o.DisableColors,
|
||||
DisableTimestamp: o.DisableTimestamp,
|
||||
FullTimestamp: o.FullTimestamp,
|
||||
CallerPrettyfier: o.CallerPrettyfier,
|
||||
},
|
||||
filter: o.Filter,
|
||||
})
|
||||
}
|
||||
|
||||
// SetReportCaller makes the process-wide Logger record where each call came from, which costs time.
|
||||
func SetReportCaller(on bool) { logrus.SetReportCaller(on) }
|
||||
|
||||
type filteringFormatter struct {
|
||||
inner logrus.Formatter
|
||||
filter func([]byte) []byte
|
||||
}
|
||||
|
||||
func (f *filteringFormatter) Format(e *logrus.Entry) ([]byte, error) {
|
||||
line, err := f.inner.Format(e)
|
||||
if err != nil || f.filter == nil {
|
||||
return line, err
|
||||
}
|
||||
return f.filter(line), nil
|
||||
}
|
||||
@@ -5,14 +5,24 @@ package logging
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var std = New(NewLogrusHandler(logrus.StandardLogger()))
|
||||
var std atomic.Pointer[Logger]
|
||||
|
||||
func init() {
|
||||
std.Store(New(NewLogrusHandler(logrus.StandardLogger())))
|
||||
}
|
||||
|
||||
// Default returns the process-wide Logger.
|
||||
func Default() *Logger { return std }
|
||||
func Default() *Logger { return std.Load() }
|
||||
|
||||
// SetDefault replaces the process-wide Logger, which tests use to capture what the package
|
||||
// functions log. SetLevel, GetLevel, SetOutput and the format functions keep configuring the
|
||||
// initial logrus-backed Logger.
|
||||
func SetDefault(l *Logger) { std.Store(l) }
|
||||
|
||||
// SetLevel sets the minimum level of the process-wide Logger.
|
||||
func SetLevel(level Level) { logrus.SetLevel(toLogrusLevel(level)) }
|
||||
@@ -26,17 +36,17 @@ func SetOutput(w io.Writer) { logrus.SetOutput(w) }
|
||||
// Output returns where the process-wide Logger writes.
|
||||
func Output() io.Writer { return logrus.StandardLogger().Out }
|
||||
|
||||
func Enabled(level Level) bool { return std.Enabled(level) }
|
||||
func Enabled(level Level) bool { return Default().Enabled(level) }
|
||||
|
||||
func With(args ...any) *Logger { return std.With(args...) }
|
||||
func WithError(err error) *Logger { return std.WithError(err) }
|
||||
func Tracef(format string, args ...any) { std.logf(LevelTrace, format, args) }
|
||||
func Debugf(format string, args ...any) { std.logf(LevelDebug, format, args) }
|
||||
func Infof(format string, args ...any) { std.logf(LevelInfo, format, args) }
|
||||
func Warnf(format string, args ...any) { std.logf(LevelWarn, format, args) }
|
||||
func Errorf(format string, args ...any) { std.logf(LevelError, format, args) }
|
||||
func Trace(args ...any) { std.log(LevelTrace, args) }
|
||||
func Debug(args ...any) { std.log(LevelDebug, args) }
|
||||
func Info(args ...any) { std.log(LevelInfo, args) }
|
||||
func Warn(args ...any) { std.log(LevelWarn, args) }
|
||||
func Error(args ...any) { std.log(LevelError, args) }
|
||||
func With(args ...any) *Logger { return Default().With(args...) }
|
||||
func WithError(err error) *Logger { return Default().WithError(err) }
|
||||
func Tracef(format string, args ...any) { Default().logf(LevelTrace, format, args) }
|
||||
func Debugf(format string, args ...any) { Default().logf(LevelDebug, format, args) }
|
||||
func Infof(format string, args ...any) { Default().logf(LevelInfo, format, args) }
|
||||
func Warnf(format string, args ...any) { Default().logf(LevelWarn, format, args) }
|
||||
func Errorf(format string, args ...any) { Default().logf(LevelError, format, args) }
|
||||
func Trace(args ...any) { Default().log(LevelTrace, args) }
|
||||
func Debug(args ...any) { Default().log(LevelDebug, args) }
|
||||
func Info(args ...any) { Default().log(LevelInfo, args) }
|
||||
func Warn(args ...any) { Default().log(LevelWarn, args) }
|
||||
func Error(args ...any) { Default().log(LevelError, args) }
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"context"
|
||||
"slices"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"gitea.com/gitea/runner/act/common/logging"
|
||||
)
|
||||
@@ -76,3 +77,13 @@ func (h *Handler) Reset() {
|
||||
defer h.store.mu.Unlock()
|
||||
h.store.records = nil
|
||||
}
|
||||
|
||||
// SetDefault makes the process-wide Logger record into a new Handler until the test ends.
|
||||
func SetDefault(t testing.TB) *Handler {
|
||||
t.Helper()
|
||||
prev := logging.Default()
|
||||
logger, h := NewLogger()
|
||||
logging.SetDefault(logger)
|
||||
t.Cleanup(func() { logging.SetDefault(prev) })
|
||||
return h
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"gitea.com/gitea/runner/internal/app/run"
|
||||
"gitea.com/gitea/runner/internal/pkg/config"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -67,7 +66,7 @@ func runCacheServer(configFile *string, cacheArgs *cacheServerArgs) func(cmd *co
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("cache server is listening on %v", cacheHandler.ExternalURL())
|
||||
logging.Infof("cache server is listening on %v", cacheHandler.ExternalURL())
|
||||
|
||||
<-cmd.Context().Done()
|
||||
|
||||
|
||||
+39
-39
@@ -18,6 +18,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.com/gitea/runner/act/common/logging"
|
||||
"gitea.com/gitea/runner/internal/app/poll"
|
||||
"gitea.com/gitea/runner/internal/app/run"
|
||||
"gitea.com/gitea/runner/internal/pkg/client"
|
||||
@@ -34,7 +35,6 @@ import (
|
||||
runnerv1 "gitea.dev/actionslib/runner/v1"
|
||||
"github.com/avast/retry-go/v5"
|
||||
"github.com/mattn/go-isatty"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -46,11 +46,11 @@ func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) fu
|
||||
}
|
||||
|
||||
initLogging(cfg)
|
||||
log.Infoln("Starting runner daemon")
|
||||
logging.Info("Starting runner daemon")
|
||||
|
||||
reg, err := config.LoadRegistration(cfg.Runner.File)
|
||||
if os.IsNotExist(err) {
|
||||
log.Error("registration file not found, please register the runner first")
|
||||
logging.Error("registration file not found, please register the runner first")
|
||||
return err
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("failed to load registration file: %w", err)
|
||||
@@ -62,13 +62,13 @@ func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) fu
|
||||
releaseLock, err := lock.TryLock(cfg.Runner.File)
|
||||
switch {
|
||||
case errors.Is(err, lock.ErrLocked):
|
||||
log.Errorf("another gitea-runner process is already using %q; each runner process needs its own runner file (runner.file)", cfg.Runner.File)
|
||||
logging.Errorf("another gitea-runner process is already using %q; each runner process needs its own runner file (runner.file)", cfg.Runner.File)
|
||||
return err
|
||||
case err != nil:
|
||||
// Best-effort guard: if the lock file can't be created (e.g. a
|
||||
// read-only runner-file mount), warn and start anyway rather than
|
||||
// refusing to run.
|
||||
log.Warnf("could not lock runner file %q, continuing without the single-process guard: %v", cfg.Runner.File, err)
|
||||
logging.Warnf("could not lock runner file %q, continuing without the single-process guard: %v", cfg.Runner.File, err)
|
||||
default:
|
||||
// Held until shutdown finishes: the draining runner still owns this
|
||||
// identity on the server, so releasing early would let a restart
|
||||
@@ -78,13 +78,13 @@ func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) fu
|
||||
|
||||
shutdownTelemetry, err := telemetry.Setup(ctx, reg.UUID, reg.Name, metrics.Registry)
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("OpenTelemetry export failed to start")
|
||||
logging.WithError(err).Warn("OpenTelemetry export failed to start")
|
||||
}
|
||||
defer func() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if err := shutdownTelemetry(ctx); err != nil {
|
||||
log.WithError(err).Warn("OpenTelemetry export failed to flush")
|
||||
logging.WithError(err).Warn("OpenTelemetry export failed to flush")
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -94,13 +94,13 @@ func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) fu
|
||||
for _, l := range lbls {
|
||||
label, err := labels.Parse(l)
|
||||
if err != nil {
|
||||
log.WithError(err).Warnf("ignored invalid label %q", l)
|
||||
logging.WithError(err).Warnf("ignored invalid label %q", l)
|
||||
continue
|
||||
}
|
||||
ls = append(ls, label)
|
||||
}
|
||||
if len(ls) == 0 {
|
||||
log.Warn("no labels configured, runner may not be able to pick up jobs")
|
||||
logging.Warn("no labels configured, runner may not be able to pick up jobs")
|
||||
}
|
||||
|
||||
// Before the first Docker API call: the standard library resolves the proxy
|
||||
@@ -108,7 +108,7 @@ func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) fu
|
||||
if dockerSocketPath, err := getDockerSocketPath(cfg.Container.DockerHost); err == nil {
|
||||
run.BypassProxyForDockerHost(dockerSocketPath)
|
||||
} else {
|
||||
log.Debugf("cannot resolve the docker socket path, so Docker API calls are not exempted from the proxy: %v", err)
|
||||
logging.Debugf("cannot resolve the docker socket path, so Docker API calls are not exempted from the proxy: %v", err)
|
||||
}
|
||||
|
||||
if ls.RequireDocker() || cfg.Container.RequireDocker {
|
||||
@@ -120,20 +120,20 @@ func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) fu
|
||||
for keepRunning {
|
||||
dockerSocketPath, err := getDockerSocketPath(cfg.Container.DockerHost)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get socket path: %s", err.Error())
|
||||
logging.Errorf("Failed to get socket path: %s", err.Error())
|
||||
} else if err = envcheck.CheckIfDockerRunning(tctx, dockerSocketPath); errors.Is(err, context.Canceled) {
|
||||
log.Infof("Docker wait timeout of %s expired", timeout.String())
|
||||
logging.Infof("Docker wait timeout of %s expired", timeout.String())
|
||||
break
|
||||
} else if err != nil {
|
||||
log.Errorf("Docker connection failed: %s", err.Error())
|
||||
logging.Errorf("Docker connection failed: %s", err.Error())
|
||||
} else {
|
||||
log.Infof("Docker is ready")
|
||||
logging.Infof("Docker is ready")
|
||||
break
|
||||
}
|
||||
select {
|
||||
case <-time.After(time.Second):
|
||||
case <-tctx.Done():
|
||||
log.Infof("Docker wait timeout of %s expired", timeout.String())
|
||||
logging.Infof("Docker wait timeout of %s expired", timeout.String())
|
||||
keepRunning = false
|
||||
}
|
||||
}
|
||||
@@ -169,7 +169,7 @@ func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) fu
|
||||
if err := config.SaveRegistration(cfg.Runner.File, reg); err != nil {
|
||||
return fmt.Errorf("failed to save runner config: %w", err)
|
||||
}
|
||||
log.Infof("labels updated to: %v", reg.Labels)
|
||||
logging.Infof("labels updated to: %v", reg.Labels)
|
||||
}
|
||||
|
||||
cli := client.New(
|
||||
@@ -184,7 +184,7 @@ func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) fu
|
||||
runner := run.NewRunner(cfg, reg, cli)
|
||||
defer func() {
|
||||
if err := runner.Close(); err != nil {
|
||||
log.Warnf("runner %s: cache server shutdown: %v", reg.Name, err)
|
||||
logging.Warnf("runner %s: cache server shutdown: %v", reg.Name, err)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -197,9 +197,9 @@ func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) fu
|
||||
retry.RetryIf(shouldRetryDeclare),
|
||||
retry.OnRetry(func(attempt uint, err error) {
|
||||
if attempt == 0 {
|
||||
log.WithError(err).Warn("fail to invoke Declare, retrying until Gitea is reachable")
|
||||
logging.WithError(err).Warn("fail to invoke Declare, retrying until Gitea is reachable")
|
||||
} else {
|
||||
log.WithError(err).Debug("fail to invoke Declare, retrying")
|
||||
logging.WithError(err).Debug("fail to invoke Declare, retrying")
|
||||
}
|
||||
}),
|
||||
).Do(func() (*connect.Response[runnerv1.DeclareResponse], error) {
|
||||
@@ -209,16 +209,16 @@ func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) fu
|
||||
case err != nil && ctx.Err() != nil && !client.IsRegistrationRejected(err):
|
||||
return nil
|
||||
case err != nil && connect.CodeOf(err) == connect.CodeUnimplemented:
|
||||
log.Errorf("Your Gitea version is too old to support runner declare, please upgrade to v1.21 or later")
|
||||
logging.Errorf("Your Gitea version is too old to support runner declare, please upgrade to v1.21 or later")
|
||||
return err
|
||||
case err != nil:
|
||||
log.WithError(err).Error("fail to invoke Declare")
|
||||
logging.WithError(err).Error("fail to invoke Declare")
|
||||
if client.IsRegistrationRejected(err) {
|
||||
removeEphemeralRegistration(reg, cfg.Runner.File)
|
||||
}
|
||||
return err
|
||||
default:
|
||||
log.Infof("runner: %s, with version: %s, with labels: %v, declare successfully",
|
||||
logging.Infof("runner: %s, with version: %s, with labels: %v, declare successfully",
|
||||
resp.Msg.Runner.Name, resp.Msg.Runner.Version, resp.Msg.Runner.Labels)
|
||||
}
|
||||
runner.SetCapabilitiesFromDeclare(resp)
|
||||
@@ -268,14 +268,14 @@ func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) fu
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("runner: %s shutdown initiated, waiting %s for running jobs to complete before shutting down", resp.Msg.Runner.Name, cfg.Runner.ShutdownTimeout)
|
||||
logging.Infof("runner: %s shutdown initiated, waiting %s for running jobs to complete before shutting down", resp.Msg.Runner.Name, cfg.Runner.ShutdownTimeout)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), cfg.Runner.ShutdownTimeout)
|
||||
defer cancel()
|
||||
|
||||
err = poller.Shutdown(ctx)
|
||||
if err != nil {
|
||||
log.Warnf("runner: %s cancelled in progress jobs during shutdown", resp.Msg.Runner.Name)
|
||||
logging.Warnf("runner: %s cancelled in progress jobs during shutdown", resp.Msg.Runner.Name)
|
||||
}
|
||||
|
||||
if poller.RegistrationRejected() {
|
||||
@@ -295,9 +295,9 @@ func removeEphemeralRegistration(reg *config.Registration, file string) {
|
||||
return
|
||||
}
|
||||
if err := os.Remove(file); err == nil {
|
||||
log.Infof("removed the spent ephemeral registration %q", file)
|
||||
logging.Infof("removed the spent ephemeral registration %q", file)
|
||||
} else if !os.IsNotExist(err) {
|
||||
log.WithError(err).Warn("failed to remove the spent ephemeral registration")
|
||||
logging.WithError(err).Warn("failed to remove the spent ephemeral registration")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,7 +328,7 @@ func splitLabels(s string) []string {
|
||||
return lbls
|
||||
}
|
||||
|
||||
// initLogging setup the global logrus logger.
|
||||
// initLogging sets up the process-wide logger.
|
||||
func initLogging(cfg *config.Config) {
|
||||
callPrettyfier := func(f *runtime.Frame) (string, string) {
|
||||
// get function name
|
||||
@@ -341,35 +341,35 @@ func initLogging(cfg *config.Config) {
|
||||
}
|
||||
|
||||
isTerm := isatty.IsTerminal(os.Stdout.Fd())
|
||||
format := &log.TextFormatter{
|
||||
logging.SetTextFormat(logging.TextOptions{
|
||||
DisableColors: !isTerm,
|
||||
FullTimestamp: true,
|
||||
CallerPrettyfier: callPrettyfier,
|
||||
}
|
||||
log.SetFormatter(report.MaskingFormatter(format))
|
||||
Filter: report.MaskGlobal,
|
||||
})
|
||||
|
||||
l := cfg.Log.Level
|
||||
if l == "" {
|
||||
log.Infof("Log level not set, sticking to info")
|
||||
logging.Infof("Log level not set, sticking to info")
|
||||
return
|
||||
}
|
||||
|
||||
level, err := log.ParseLevel(l)
|
||||
level, err := logging.ParseLevel(l)
|
||||
if err != nil {
|
||||
log.WithError(err).
|
||||
logging.WithError(err).
|
||||
Errorf("invalid log level: %q", l)
|
||||
}
|
||||
|
||||
// debug level
|
||||
switch level {
|
||||
case log.DebugLevel, log.TraceLevel:
|
||||
log.SetReportCaller(true) // Only in debug or trace because it takes a performance toll
|
||||
log.Infof("Log level %s requested, setting up report caller for further debugging", level)
|
||||
case logging.LevelDebug, logging.LevelTrace:
|
||||
logging.SetReportCaller(true) // Only in debug or trace because it takes a performance toll
|
||||
logging.Infof("Log level %s requested, setting up report caller for further debugging", level)
|
||||
}
|
||||
|
||||
if log.GetLevel() != level {
|
||||
log.Infof("log level set to %v", level)
|
||||
log.SetLevel(level)
|
||||
if logging.GetLevel() != level {
|
||||
logging.Infof("log level set to %v", level)
|
||||
logging.SetLevel(level)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,17 +11,17 @@ import (
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitea.com/gitea/runner/act/common/logging"
|
||||
"gitea.com/gitea/runner/internal/pkg/config"
|
||||
"gitea.com/gitea/runner/internal/pkg/report"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
runnerv1 "gitea.dev/actionslib/runner/v1"
|
||||
"gitea.dev/actionslib/runner/v1/runnerv1connect"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
@@ -64,24 +64,23 @@ func TestGetDockerSocketPathUsesConfigAndEnvironment(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInitLoggingSetsLevelAndCaller(t *testing.T) {
|
||||
oldLevel := log.GetLevel()
|
||||
oldReportCaller := log.StandardLogger().ReportCaller
|
||||
oldLevel, oldOut := logging.GetLevel(), logging.Output()
|
||||
t.Cleanup(func() {
|
||||
log.SetLevel(oldLevel)
|
||||
log.SetReportCaller(oldReportCaller)
|
||||
logging.SetLevel(oldLevel)
|
||||
logging.SetOutput(oldOut)
|
||||
logging.SetReportCaller(false)
|
||||
logging.SetTextFormat(logging.TextOptions{})
|
||||
})
|
||||
|
||||
oldFormatter := log.StandardLogger().Formatter
|
||||
t.Cleanup(func() { log.SetFormatter(oldFormatter) })
|
||||
|
||||
cfg := &config.Config{}
|
||||
cfg.Log.Level = "debug"
|
||||
initLogging(cfg)
|
||||
|
||||
require.Equal(t, log.DebugLevel, log.GetLevel())
|
||||
require.True(t, log.StandardLogger().ReportCaller)
|
||||
// act plans a job on this logger, so a live task's secrets have to be masked out of it
|
||||
require.IsType(t, report.MaskingFormatter(nil), log.StandardLogger().Formatter)
|
||||
require.Equal(t, logging.LevelDebug, logging.GetLevel())
|
||||
var buf strings.Builder
|
||||
logging.SetOutput(&buf)
|
||||
logging.Debugf("line")
|
||||
require.Contains(t, buf.String(), "[daemon_test.go:")
|
||||
}
|
||||
|
||||
var errUnregisteredRunner = errors.New("rpc error: code = Unauthenticated desc = unregistered runner")
|
||||
|
||||
+19
-18
@@ -31,7 +31,6 @@ import (
|
||||
"gitea.dev/actionslib/pkg/model"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
@@ -106,7 +105,7 @@ func (i *executeArgs) LoadSecrets() map[string]string {
|
||||
secretPairParts := strings.SplitN(secretPair, "=", 2)
|
||||
secretPairParts[0] = strings.ToUpper(secretPairParts[0])
|
||||
if strings.EqualFold(s[secretPairParts[0]], secretPairParts[0]) {
|
||||
log.Errorf("Secret %s is already defined (secrets are case insensitive)", secretPairParts[0])
|
||||
logging.Errorf("Secret %s is already defined (secrets are case insensitive)", secretPairParts[0])
|
||||
}
|
||||
if len(secretPairParts) == 2 {
|
||||
s[secretPairParts[0]] = secretPairParts[1]
|
||||
@@ -117,7 +116,7 @@ func (i *executeArgs) LoadSecrets() map[string]string {
|
||||
val, err := term.ReadPassword(int(os.Stdin.Fd()))
|
||||
fmt.Println()
|
||||
if err != nil {
|
||||
log.Errorf("failed to read input: %v", err)
|
||||
logging.Errorf("failed to read input: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
s[secretPairParts[0]] = string(val)
|
||||
@@ -130,7 +129,8 @@ func readEnvs(path string, envs map[string]string) bool {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
env, err := godotenv.Read(path)
|
||||
if err != nil {
|
||||
log.Fatalf("Error loading from %s: %v", path, err)
|
||||
logging.Errorf("Error loading from %s: %v", path, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
maps.Copy(envs, env)
|
||||
return true
|
||||
@@ -221,7 +221,8 @@ func (i *executeArgs) Workdir() string {
|
||||
func (i *executeArgs) resolve(path string) string {
|
||||
basedir, err := filepath.Abs(i.workdir)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
logging.Error(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if path == "" {
|
||||
return path
|
||||
@@ -337,7 +338,7 @@ func runExecList(planner model.WorkflowPlanner, execArgs *executeArgs) error {
|
||||
var filterEventName string
|
||||
|
||||
if len(execArgs.event) > 0 {
|
||||
log.Infof("Using chosed event for filtering: %s", execArgs.event)
|
||||
logging.Infof("Using chosed event for filtering: %s", execArgs.event)
|
||||
filterEventName = execArgs.event
|
||||
} else if execArgs.autodetectEvent {
|
||||
// collect all events from loaded workflows
|
||||
@@ -345,7 +346,7 @@ func runExecList(planner model.WorkflowPlanner, execArgs *executeArgs) error {
|
||||
|
||||
// set default event type to first event from many available
|
||||
// this way user dont have to specify the event.
|
||||
log.Infof("Using first detected workflow event for filtering: %s", events[0])
|
||||
logging.Infof("Using first detected workflow event for filtering: %s", events[0])
|
||||
|
||||
filterEventName = events[0]
|
||||
}
|
||||
@@ -353,19 +354,19 @@ func runExecList(planner model.WorkflowPlanner, execArgs *executeArgs) error {
|
||||
var err error
|
||||
switch {
|
||||
case execArgs.job != "":
|
||||
log.Infof("Preparing plan with a job: %s", execArgs.job)
|
||||
logging.Infof("Preparing plan with a job: %s", execArgs.job)
|
||||
filterPlan, err = planner.PlanJob(execArgs.job)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case filterEventName != "":
|
||||
log.Infof("Preparing plan for a event: %s", filterEventName)
|
||||
logging.Infof("Preparing plan for a event: %s", filterEventName)
|
||||
filterPlan, err = planner.PlanEvent(filterEventName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
log.Infof("Preparing plan with all jobs")
|
||||
logging.Infof("Preparing plan with all jobs")
|
||||
filterPlan, err = planner.PlanAll()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -445,30 +446,30 @@ func runExec(ctx context.Context, execArgs *executeArgs) func(cmd *cobra.Command
|
||||
|
||||
switch {
|
||||
case len(execArgs.event) > 0:
|
||||
log.Infof("Using chosed event for filtering: %s", execArgs.event)
|
||||
logging.Infof("Using chosed event for filtering: %s", execArgs.event)
|
||||
eventName = execArgs.event
|
||||
case len(events) == 1 && len(events[0]) > 0:
|
||||
log.Infof("Using the only detected workflow event: %s", events[0])
|
||||
logging.Infof("Using the only detected workflow event: %s", events[0])
|
||||
eventName = events[0]
|
||||
case execArgs.autodetectEvent && len(events) > 0 && len(events[0]) > 0:
|
||||
// set default event type to first event from many available
|
||||
// this way user dont have to specify the event.
|
||||
log.Infof("Using first detected workflow event: %s", events[0])
|
||||
logging.Infof("Using first detected workflow event: %s", events[0])
|
||||
eventName = events[0]
|
||||
default:
|
||||
log.Infof("Using default workflow event: push")
|
||||
logging.Infof("Using default workflow event: push")
|
||||
eventName = "push"
|
||||
}
|
||||
|
||||
// build the plan for this run
|
||||
if execArgs.job != "" {
|
||||
log.Infof("Planning job: %s", execArgs.job)
|
||||
logging.Infof("Planning job: %s", execArgs.job)
|
||||
plan, err = planner.PlanJob(execArgs.job)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
log.Infof("Planning jobs for event: %s", eventName)
|
||||
logging.Infof("Planning jobs for event: %s", eventName)
|
||||
plan, err = planner.PlanEvent(eventName)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -488,7 +489,7 @@ func runExec(ctx context.Context, execArgs *executeArgs) func(cmd *cobra.Command
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("cache handler listens on: %v", handler.ExternalURL())
|
||||
logging.Infof("cache handler listens on: %v", handler.ExternalURL())
|
||||
execArgs.cacheHandler = handler
|
||||
|
||||
if len(execArgs.artifactServerAddr) == 0 {
|
||||
@@ -564,7 +565,7 @@ func runExec(ctx context.Context, execArgs *executeArgs) func(cmd *cobra.Command
|
||||
}
|
||||
|
||||
artifactCancel := artifacts.Serve(ctx, execArgs.artifactServerPath, execArgs.artifactServerAddr, execArgs.artifactServerPort)
|
||||
log.Debugf("artifacts server started at %s:%s", execArgs.artifactServerPath, execArgs.artifactServerPort)
|
||||
logging.Debugf("artifacts server started at %s:%s", execArgs.artifactServerPath, execArgs.artifactServerPort)
|
||||
|
||||
ctx = common.WithDryrun(ctx, execArgs.dryrun)
|
||||
executor := r.NewPlanExecutor(plan).Finally(func(ctx context.Context) error {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
goruntime "runtime"
|
||||
"strings"
|
||||
|
||||
"gitea.com/gitea/runner/act/common/logging"
|
||||
"gitea.com/gitea/runner/internal/app/run"
|
||||
"gitea.com/gitea/runner/internal/pkg/client"
|
||||
"gitea.com/gitea/runner/internal/pkg/config"
|
||||
@@ -24,28 +25,27 @@ import (
|
||||
pingv1 "gitea.dev/actionslib/ping/v1"
|
||||
runnerv1 "gitea.dev/actionslib/runner/v1"
|
||||
"github.com/mattn/go-isatty"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// runRegister registers a runner to the server
|
||||
func runRegister(ctx context.Context, regArgs *registerArgs, configFile *string) func(*cobra.Command, []string) error {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
log.SetReportCaller(false)
|
||||
logging.SetReportCaller(false)
|
||||
isTerm := isatty.IsTerminal(os.Stdout.Fd())
|
||||
log.SetFormatter(&log.TextFormatter{
|
||||
logging.SetTextFormat(logging.TextOptions{
|
||||
DisableColors: !isTerm,
|
||||
DisableTimestamp: true,
|
||||
})
|
||||
log.SetLevel(log.DebugLevel)
|
||||
logging.SetLevel(logging.LevelDebug)
|
||||
|
||||
log.Infof("Registering runner, arch=%s, os=%s, version=%s.",
|
||||
logging.Infof("Registering runner, arch=%s, os=%s, version=%s.",
|
||||
goruntime.GOARCH, goruntime.GOOS, ver.Version())
|
||||
|
||||
// runner always needs root permission
|
||||
if os.Getuid() != 0 {
|
||||
// TODO: use a better way to check root permission
|
||||
log.Warnf("Runner in user-mode.")
|
||||
logging.Warnf("Runner in user-mode.")
|
||||
}
|
||||
|
||||
if regArgs.NoInteractive {
|
||||
@@ -55,7 +55,8 @@ func runRegister(ctx context.Context, regArgs *registerArgs, configFile *string)
|
||||
} else {
|
||||
go func() {
|
||||
if err := registerInteractive(ctx, *configFile, regArgs); err != nil {
|
||||
log.Fatal(err)
|
||||
logging.Error(err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
os.Exit(0)
|
||||
@@ -182,13 +183,13 @@ func (r *registerInputs) assignToNext(stage registerStage, value string, cfg *co
|
||||
for _, l := range cfg.Runner.Labels {
|
||||
_, err := labels.Parse(l)
|
||||
if err != nil {
|
||||
log.WithError(err).Warnf("ignored invalid label %q", l)
|
||||
logging.WithError(err).Warnf("ignored invalid label %q", l)
|
||||
continue
|
||||
}
|
||||
ls = append(ls, l)
|
||||
}
|
||||
if len(ls) == 0 {
|
||||
log.Warn("no valid labels configured in config file, runner may not be able to pick up jobs")
|
||||
logging.Warn("no valid labels configured in config file, runner may not be able to pick up jobs")
|
||||
}
|
||||
r.Labels = ls
|
||||
return StageWaitingForRegistration
|
||||
@@ -201,7 +202,7 @@ func (r *registerInputs) assignToNext(stage registerStage, value string, cfg *co
|
||||
}
|
||||
|
||||
if validateLabels(r.Labels) != nil {
|
||||
log.Infoln("Invalid labels, please input again, leave blank to use the default labels (for example, ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest)")
|
||||
logging.Info("Invalid labels, please input again, leave blank to use the default labels (for example, ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest)")
|
||||
r.Labels = nil
|
||||
return StageInputLabels
|
||||
}
|
||||
@@ -273,11 +274,11 @@ func registerInteractive(ctx context.Context, configFile string, regArgs *regist
|
||||
stage = inputs.assignToNext(stage, strings.TrimSpace(cmdString), cfg)
|
||||
|
||||
if stage == StageWaitingForRegistration {
|
||||
log.Infof("Registering runner, name=%s, instance=%s, labels=%v.", inputs.RunnerName, inputs.InstanceAddr, inputs.Labels)
|
||||
logging.Infof("Registering runner, name=%s, instance=%s, labels=%v.", inputs.RunnerName, inputs.InstanceAddr, inputs.Labels)
|
||||
if err := doRegister(ctx, cfg, inputs); err != nil {
|
||||
return fmt.Errorf("failed to register runner: %w", err)
|
||||
}
|
||||
log.Infof("Runner registered successfully.")
|
||||
logging.Infof("Runner registered successfully.")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -286,7 +287,7 @@ func registerInteractive(ctx context.Context, configFile string, regArgs *regist
|
||||
}
|
||||
|
||||
if stage <= StageUnknown {
|
||||
log.Errorf("Invalid input, please re-run act command.")
|
||||
logging.Errorf("Invalid input, please re-run act command.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -295,18 +296,18 @@ func registerInteractive(ctx context.Context, configFile string, regArgs *regist
|
||||
func printStageHelp(stage registerStage) {
|
||||
switch stage {
|
||||
case StageOverwriteLocalConfig:
|
||||
log.Infoln("Runner is already registered, overwrite local config? [y/N]")
|
||||
logging.Info("Runner is already registered, overwrite local config? [y/N]")
|
||||
case StageInputInstance:
|
||||
log.Infoln("Enter the Gitea instance URL (for example, https://gitea.com/):")
|
||||
logging.Info("Enter the Gitea instance URL (for example, https://gitea.com/):")
|
||||
case StageInputToken:
|
||||
log.Infoln("Enter the runner token:")
|
||||
logging.Info("Enter the runner token:")
|
||||
case StageInputRunnerName:
|
||||
hostname, _ := os.Hostname()
|
||||
log.Infof("Enter the runner name (if set empty, use hostname: %s):\n", hostname)
|
||||
logging.Infof("Enter the runner name (if set empty, use hostname: %s):\n", hostname)
|
||||
case StageInputLabels:
|
||||
log.Infoln("Enter the runner labels, leave blank to use the default labels (comma-separated, for example, ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest):")
|
||||
logging.Info("Enter the runner labels, leave blank to use the default labels (comma-separated, for example, ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest):")
|
||||
case StageWaitingForRegistration:
|
||||
log.Infoln("Waiting for registration...")
|
||||
logging.Info("Waiting for registration...")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +323,7 @@ func registerNoInteractive(ctx context.Context, configFile string, regArgs *regi
|
||||
// specify labels in config file.
|
||||
if len(cfg.Runner.Labels) > 0 {
|
||||
if regArgs.Labels != "" {
|
||||
log.Warn("Labels from command will be ignored, use labels defined in config file.")
|
||||
logging.Warn("Labels from command will be ignored, use labels defined in config file.")
|
||||
}
|
||||
inputs.Labels = cfg.Runner.Labels
|
||||
}
|
||||
@@ -332,16 +333,16 @@ func registerNoInteractive(ctx context.Context, configFile string, regArgs *regi
|
||||
|
||||
if inputs.RunnerName == "" {
|
||||
inputs.RunnerName, _ = os.Hostname()
|
||||
log.Infof("Runner name is empty, use hostname '%s'.", inputs.RunnerName)
|
||||
logging.Infof("Runner name is empty, use hostname '%s'.", inputs.RunnerName)
|
||||
}
|
||||
if err := inputs.validate(); err != nil {
|
||||
log.WithError(err).Errorf("Invalid input, please re-run act command.")
|
||||
logging.WithError(err).Errorf("Invalid input, please re-run act command.")
|
||||
return err
|
||||
}
|
||||
if err := doRegister(ctx, cfg, inputs); err != nil {
|
||||
return fmt.Errorf("failed to register runner: %w", err)
|
||||
}
|
||||
log.Infof("Runner registered successfully.")
|
||||
logging.Infof("Runner registered successfully.")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -355,7 +356,7 @@ func doRegister(ctx context.Context, cfg *config.Config, inputs *registerInputs)
|
||||
// Best-effort guard: if the lock file can't be created, warn and
|
||||
// register anyway; writing the runner file will surface any real
|
||||
// permission problem with a clearer error.
|
||||
log.Warnf("could not lock runner file %q, continuing without the single-process guard: %v", cfg.Runner.File, err)
|
||||
logging.Warnf("could not lock runner file %q, continuing without the single-process guard: %v", cfg.Runner.File, err)
|
||||
default:
|
||||
defer func() { _ = releaseLock() }()
|
||||
}
|
||||
@@ -375,7 +376,7 @@ func doRegister(ctx context.Context, cfg *config.Config, inputs *registerInputs)
|
||||
})); err != nil {
|
||||
return fmt.Errorf("cannot ping the Gitea instance server: %w", err)
|
||||
}
|
||||
log.Debugln("Successfully pinged the Gitea instance server")
|
||||
logging.Debug("Successfully pinged the Gitea instance server")
|
||||
|
||||
reg := &config.Registration{
|
||||
Name: inputs.RunnerName,
|
||||
@@ -403,7 +404,7 @@ func doRegister(ctx context.Context, cfg *config.Config, inputs *registerInputs)
|
||||
Capabilities: run.RunnerCapabilities(),
|
||||
}))
|
||||
if err != nil {
|
||||
log.WithError(err).Error("poller: cannot register new runner")
|
||||
logging.WithError(err).Error("poller: cannot register new runner")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -414,7 +415,7 @@ func doRegister(ctx context.Context, cfg *config.Config, inputs *registerInputs)
|
||||
|
||||
if inputs.Ephemeral != resp.Msg.Runner.Ephemeral {
|
||||
// TODO we cannot remove the configuration via runner api, if we return an error here we just fill the database
|
||||
log.Error("poller: cannot register new runner as ephemeral upgrade Gitea to gain security, run-once will be used automatically")
|
||||
logging.Error("poller: cannot register new runner as ephemeral upgrade Gitea to gain security, run-once will be used automatically")
|
||||
}
|
||||
|
||||
if err := config.SaveRegistration(cfg.Runner.File, reg); err != nil {
|
||||
|
||||
@@ -12,13 +12,13 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"gitea.com/gitea/runner/act/common/logging"
|
||||
"gitea.com/gitea/runner/internal/pkg/client"
|
||||
"gitea.com/gitea/runner/internal/pkg/config"
|
||||
"gitea.com/gitea/runner/internal/pkg/metrics"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
runnerv1 "gitea.dev/actionslib/runner/v1"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// TaskRunner abstracts task execution so the poller can be tested
|
||||
@@ -236,9 +236,9 @@ func (p *Poller) reportAvailability(ready bool, reason string) {
|
||||
defer p.availabilityMu.Unlock()
|
||||
switch {
|
||||
case !ready && p.availabilityReady:
|
||||
log.Warnf("runner temporarily unavailable: %s", reason)
|
||||
logging.Warnf("runner temporarily unavailable: %s", reason)
|
||||
case ready && !p.availabilityReady:
|
||||
log.Info("runner local health recovered, resuming task polling")
|
||||
logging.Info("runner local health recovered, resuming task polling")
|
||||
}
|
||||
p.availabilityReady = ready
|
||||
p.availabilityReason = reason
|
||||
@@ -339,12 +339,12 @@ func (p *Poller) runTaskWithRecover(ctx context.Context, task *runnerv1.Task) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err := fmt.Errorf("panic: %v", r)
|
||||
log.WithError(err).Error("panic in runTaskWithRecover")
|
||||
logging.WithError(err).Error("panic in runTaskWithRecover")
|
||||
}
|
||||
}()
|
||||
|
||||
if err := p.runner.Run(ctx, task); err != nil {
|
||||
log.WithError(err).Error("failed to run task")
|
||||
logging.WithError(err).Error("failed to run task")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ func (p *Poller) fetchTask(ctx context.Context, s *workerState) (*runnerv1.Task,
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
if !s.fetchTimedOut {
|
||||
s.fetchTimedOut = true
|
||||
log.Warnf("fetching a task timed out after %s, raise runner.fetch_timeout if this persists", p.cfg.Runner.FetchTimeout)
|
||||
logging.Warnf("fetching a task timed out after %s, raise runner.fetch_timeout if this persists", p.cfg.Runner.FetchTimeout)
|
||||
}
|
||||
s.consecutiveEmpty++
|
||||
metrics.PollFetchTotal.WithLabelValues(metrics.LabelResultEmpty).Inc()
|
||||
@@ -381,7 +381,7 @@ func (p *Poller) fetchTask(ctx context.Context, s *workerState) (*runnerv1.Task,
|
||||
// polling and let the daemon exit with an error instead of spinning.
|
||||
rejected := client.IsRegistrationRejected(err)
|
||||
if rejected || connect.CodeOf(err) == connect.CodeUnauthenticated {
|
||||
log.WithError(err).Error("server rejected the runner as unregistered, stopping poller")
|
||||
logging.WithError(err).Error("server rejected the runner as unregistered, stopping poller")
|
||||
p.unregistered.Store(true)
|
||||
p.registrationRejected.Store(rejected)
|
||||
p.shutdownPolling()
|
||||
@@ -390,9 +390,9 @@ func (p *Poller) fetchTask(ctx context.Context, s *workerState) (*runnerv1.Task,
|
||||
// Not a long poll, so a deadline can mean the server assigned a task
|
||||
// this runner never received.
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
log.WithError(err).Errorf("fetching a task timed out after %s", p.cfg.Runner.FetchTimeout)
|
||||
logging.WithError(err).Errorf("fetching a task timed out after %s", p.cfg.Runner.FetchTimeout)
|
||||
} else {
|
||||
log.WithError(err).Error("failed to fetch task")
|
||||
logging.WithError(err).Error("failed to fetch task")
|
||||
}
|
||||
p.lastPollFailed.Store(true)
|
||||
s.consecutiveErrors++
|
||||
|
||||
@@ -11,12 +11,12 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitea.com/gitea/runner/act/common/logging/logtest"
|
||||
"gitea.com/gitea/runner/internal/pkg/client/mocks"
|
||||
"gitea.com/gitea/runner/internal/pkg/config"
|
||||
|
||||
connect_go "connectrpc.com/connect"
|
||||
runnerv1 "gitea.dev/actionslib/runner/v1"
|
||||
"github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -87,8 +87,7 @@ func TestPoller_FetchTimeoutIsNoSignal(t *testing.T) {
|
||||
p := &Poller{client: client, cfg: cfg}
|
||||
p.lastPollFailed.Store(true)
|
||||
|
||||
hook := test.NewGlobal()
|
||||
defer hook.Reset()
|
||||
hook := logtest.SetDefault(t)
|
||||
|
||||
s := &workerState{consecutiveErrors: 2}
|
||||
_, ok := p.fetchTask(context.Background(), s)
|
||||
@@ -99,7 +98,7 @@ func TestPoller_FetchTimeoutIsNoSignal(t *testing.T) {
|
||||
|
||||
// An idle runner against a server holding the request open must not flood.
|
||||
_, _ = p.fetchTask(context.Background(), s)
|
||||
assert.Len(t, hook.AllEntries(), 1)
|
||||
assert.Len(t, hook.AllRecords(), 1)
|
||||
}
|
||||
|
||||
// TestPoller_FetchUnauthenticatedStopsPolling verifies that an Unauthenticated
|
||||
|
||||
@@ -14,13 +14,13 @@ import (
|
||||
"time"
|
||||
|
||||
"gitea.com/gitea/runner/act/common"
|
||||
"gitea.com/gitea/runner/act/common/logging"
|
||||
"gitea.com/gitea/runner/internal/pkg/config"
|
||||
"gitea.com/gitea/runner/internal/pkg/metrics"
|
||||
"gitea.com/gitea/runner/internal/pkg/process"
|
||||
"gitea.com/gitea/runner/internal/pkg/report"
|
||||
|
||||
runnerv1 "gitea.dev/actionslib/runner/v1"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (r *Runner) runPostTaskScript(ctx context.Context, reporter *report.Reporter, task *runnerv1.Task, workdir string) {
|
||||
@@ -38,7 +38,7 @@ func (r *Runner) runPostTaskScript(ctx context.Context, reporter *report.Reporte
|
||||
defer cancel()
|
||||
|
||||
env := r.postTaskScriptEnv(reporter, task, workdir)
|
||||
log.Infof("running post-task script %q for task %d", script, task.Id)
|
||||
logging.Infof("running post-task script %q for task %d", script, task.Id)
|
||||
|
||||
cmd := exec.CommandContext(scriptCtx, script)
|
||||
cmd.Env = envListFromMap(env)
|
||||
@@ -55,11 +55,11 @@ func (r *Runner) runPostTaskScript(ctx context.Context, reporter *report.Reporte
|
||||
treeKill := process.NewTreeKill(cmd)
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Warnf("post-task script %q for task %d: %v", script, task.Id, err)
|
||||
logging.Warnf("post-task script %q for task %d: %v", script, task.Id, err)
|
||||
return
|
||||
}
|
||||
if k, kerr := treeKill.Capture(cmd.Process); kerr != nil {
|
||||
log.Warnf("post-task script %q for task %d: process tree kill setup failed, falling back to single-process kill: %v", script, task.Id, kerr)
|
||||
logging.Warnf("post-task script %q for task %d: process tree kill setup failed, falling back to single-process kill: %v", script, task.Id, kerr)
|
||||
} else {
|
||||
defer k.Close()
|
||||
}
|
||||
@@ -71,14 +71,14 @@ func (r *Runner) runPostTaskScript(ctx context.Context, reporter *report.Reporte
|
||||
common.FlushWriter(stderr)
|
||||
if err != nil {
|
||||
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
||||
log.Warnf("post-task script %q for task %d: %v", script, task.Id, err)
|
||||
logging.Warnf("post-task script %q for task %d: %v", script, task.Id, err)
|
||||
return
|
||||
}
|
||||
if exitErr, ok := errors.AsType[*exec.ExitError](err); ok {
|
||||
log.Warnf("post-task script %q for task %d exited with code %d", script, task.Id, exitErr.ExitCode())
|
||||
logging.Warnf("post-task script %q for task %d exited with code %d", script, task.Id, exitErr.ExitCode())
|
||||
return
|
||||
}
|
||||
log.Warnf("post-task script %q for task %d: %v", script, task.Id, err)
|
||||
logging.Warnf("post-task script %q for task %d: %v", script, task.Id, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ func envListFromMap(env map[string]string) []string {
|
||||
// cmd.Wait() returns.
|
||||
func postTaskScriptLogWriter(stream string) io.Writer {
|
||||
return common.NewLineWriter(func(line string) bool {
|
||||
log.Infof("post-task script %s: %s", stream, strings.TrimRight(line, "\r\n"))
|
||||
logging.Infof("post-task script %s: %s", stream, strings.TrimRight(line, "\r\n"))
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"gitea.com/gitea/runner/act/common/logging"
|
||||
"gitea.com/gitea/runner/act/container"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/http/httpproxy"
|
||||
)
|
||||
|
||||
@@ -87,10 +87,10 @@ func BypassProxyForDockerHost(dockerHost string) {
|
||||
noProxy := appendNoProxy(cfg.NoProxy, host)
|
||||
for _, name := range []string{"no_proxy", "NO_PROXY"} {
|
||||
if err := os.Setenv(name, noProxy); err != nil {
|
||||
log.Warnf("cannot set %s for the runner process: %v", name, err)
|
||||
logging.Warnf("cannot set %s for the runner process: %v", name, err)
|
||||
}
|
||||
}
|
||||
log.Debugf("docker host %s is reached directly, no_proxy is now %q", host, noProxy)
|
||||
logging.Debugf("docker host %s is reached directly, no_proxy is now %q", host, noProxy)
|
||||
}
|
||||
|
||||
// WarnIfDaemonHasNoProxy points at the one part the runner cannot set: the docker daemon
|
||||
@@ -101,11 +101,11 @@ func WarnIfDaemonHasNoProxy(ctx context.Context) {
|
||||
}
|
||||
info, err := container.GetHostInfo(ctx)
|
||||
if err != nil {
|
||||
log.Debugf("cannot read the docker daemon's proxy configuration: %v", err)
|
||||
logging.Debugf("cannot read the docker daemon's proxy configuration: %v", err)
|
||||
return
|
||||
}
|
||||
if info.HTTPProxy == "" && info.HTTPSProxy == "" {
|
||||
log.Warn("the runner has a proxy but the docker daemon reports none, so image pulls will not use it: https://docs.docker.com/engine/daemon/proxy/")
|
||||
logging.Warn("the runner has a proxy but the docker daemon reports none, so image pulls will not use it: https://docs.docker.com/engine/daemon/proxy/")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+28
-29
@@ -45,7 +45,6 @@ import (
|
||||
"gitea.dev/actionslib/pkg/model"
|
||||
runnerv1 "gitea.dev/actionslib/runner/v1"
|
||||
docker_container "github.com/moby/moby/api/types/container"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.yaml.in/yaml/v4"
|
||||
)
|
||||
|
||||
@@ -113,7 +112,7 @@ func NewRunner(cfg *config.Config, reg *config.Registration, cli client.Client)
|
||||
Logger: logging.Default().With("module", "cache_request"),
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("cannot init cache server, it will be disabled: %v", err)
|
||||
logging.Errorf("cannot init cache server, it will be disabled: %v", err)
|
||||
// go on
|
||||
} else {
|
||||
cacheHandler = handler
|
||||
@@ -184,7 +183,7 @@ func (r *Runner) OnIdle(ctx context.Context) {
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Minute)
|
||||
defer cancel()
|
||||
if err := removeOrphanKubernetesResources(ctx, r.kubernetesOptions(nil), r.now().Add(-r.cfg.Runner.WorkdirCleanupAge)); err != nil {
|
||||
log.Warnf("failed to clean up job pods left behind by earlier jobs: %v", err)
|
||||
logging.Warnf("failed to clean up job pods left behind by earlier jobs: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,10 +194,10 @@ func (r *Runner) cleanupOrphanDockerResources(ctx context.Context) {
|
||||
}
|
||||
cutoff := r.now().Add(-r.cfg.Runner.WorkdirCleanupAge)
|
||||
if err := removeOrphanNetworks(ctx, r.uuid, r.isolatedCacheContainer(), cutoff); err != nil {
|
||||
log.Warnf("failed to clean up networks left behind by earlier jobs: %v", err)
|
||||
logging.Warnf("failed to clean up networks left behind by earlier jobs: %v", err)
|
||||
}
|
||||
if err := removeOrphanJobVolumes(ctx, r.uuid, cutoff); err != nil {
|
||||
log.Warnf("failed to clean up volumes left behind by earlier jobs: %v", err)
|
||||
logging.Warnf("failed to clean up volumes left behind by earlier jobs: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +254,7 @@ func (r *Runner) cleanupStaleDirs(ctx context.Context, root string, match func(n
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return
|
||||
}
|
||||
log.Warnf("failed to list directory %s for stale cleanup: %v", root, err)
|
||||
logging.Warnf("failed to list directory %s for stale cleanup: %v", root, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -275,7 +274,7 @@ func (r *Runner) cleanupStaleDirs(ctx context.Context, root string, match func(n
|
||||
}
|
||||
info, err := entry.Info()
|
||||
if err != nil {
|
||||
log.Warnf("failed to stat %s: %v", filepath.Join(root, entry.Name()), err)
|
||||
logging.Warnf("failed to stat %s: %v", filepath.Join(root, entry.Name()), err)
|
||||
continue
|
||||
}
|
||||
if info.ModTime().After(cutoff) {
|
||||
@@ -283,10 +282,10 @@ func (r *Runner) cleanupStaleDirs(ctx context.Context, root string, match func(n
|
||||
}
|
||||
dir := filepath.Join(root, entry.Name())
|
||||
if err := os.RemoveAll(dir); err != nil {
|
||||
log.Warnf("failed to clean stale directory %s: %v", dir, err)
|
||||
logging.Warnf("failed to clean stale directory %s: %v", dir, err)
|
||||
continue
|
||||
}
|
||||
log.Infof("cleaned stale directory %s", dir)
|
||||
logging.Infof("cleaned stale directory %s", dir)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,7 +330,7 @@ func (r *Runner) Run(ctx context.Context, task *runnerv1.Task) error {
|
||||
}
|
||||
_ = reporter.Close(lastWords)
|
||||
if err := cleanupJobVolumes(ctx, volumeCleanup); err != nil {
|
||||
log.Warnf("task %d volume cleanup after reporting: %v", task.Id, err)
|
||||
logging.Warnf("task %d volume cleanup after reporting: %v", task.Id, err)
|
||||
}
|
||||
|
||||
metrics.JobDuration.Observe(time.Since(start).Seconds())
|
||||
@@ -463,9 +462,8 @@ func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.WithFields(taskLogFields(task, jobID)).
|
||||
WithField("actions_url", r.getDefaultActionsURL(task)).
|
||||
WithField("address", r.client.Address()).
|
||||
logging.With(taskLogFields(task, jobID)...).
|
||||
With("actions_url", r.getDefaultActionsURL(task), "address", r.client.Address()).
|
||||
Info("task started")
|
||||
|
||||
plan, err := model.CombineWorkflowPlanner(workflow).PlanJob(jobID)
|
||||
@@ -648,7 +646,7 @@ func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report.
|
||||
// add logger recorders
|
||||
ctx = common.WithLoggerHandler(ctx, reporter)
|
||||
|
||||
if !log.IsLevelEnabled(log.DebugLevel) {
|
||||
if !logging.Enabled(logging.LevelDebug) {
|
||||
ctx = runner.WithJobLoggerFactory(ctx, NullLogger{})
|
||||
}
|
||||
|
||||
@@ -659,7 +657,7 @@ func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report.
|
||||
// Remove the entire task-specific directory (e.g. /workspace/<task_id>).
|
||||
taskDir := filepath.FromSlash("/" + workdirParent)
|
||||
if err := os.RemoveAll(taskDir); err != nil {
|
||||
log.Warnf("failed to clean up workspace %s: %v", taskDir, err)
|
||||
logging.Warnf("failed to clean up workspace %s: %v", taskDir, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -741,7 +739,7 @@ func (r *Runner) registerExternalCacheJob(token, repo, publicURL string, reporte
|
||||
if err := postInternalCache(base+"/_internal/register", r.cfg.Cache.ExternalSecret, map[string]any{
|
||||
"token": token, "repo": repo, "public_url": publicURL,
|
||||
}); err != nil {
|
||||
log.Warnf("cache external_server register failed (%s): %v", base, err)
|
||||
logging.Warnf("cache external_server register failed (%s): %v", base, err)
|
||||
if reporter != nil {
|
||||
reporter.Logf("::warning::%s", runner.EscapeCommandData(fmt.Sprintf(
|
||||
"cache external_server register failed (%s): %v — cache requests from this job will be unauthenticated and likely return 401", base, err)))
|
||||
@@ -750,7 +748,7 @@ func (r *Runner) registerExternalCacheJob(token, repo, publicURL string, reporte
|
||||
return func() {
|
||||
if err := postInternalCache(base+"/_internal/revoke", r.cfg.Cache.ExternalSecret,
|
||||
map[string]any{"token": token}); err != nil {
|
||||
log.Warnf("cache external_server revoke failed (%s): %v", base, err)
|
||||
logging.Warnf("cache external_server revoke failed (%s): %v", base, err)
|
||||
if reporter != nil {
|
||||
reporter.Logf("::warning::%s", runner.EscapeCommandData(fmt.Sprintf(
|
||||
"cache external_server revoke failed (%s): %v", base, err)))
|
||||
@@ -893,9 +891,9 @@ func warnIgnoredCachePolicy(cfg *config.Config) {
|
||||
cfg.Cache.SizeLimit != defaults.SizeLimit
|
||||
switch {
|
||||
case cfg.Cache.ExternalServer != "" && (limits || cfg.Cache.SweepInterval != defaults.SweepInterval):
|
||||
log.Warn("cache eviction settings are ignored when cache.external_server is set; configure them on that server instead")
|
||||
logging.Warn("cache eviction settings are ignored when cache.external_server is set; configure them on that server instead")
|
||||
case cfg.Cache.S3 != nil && limits:
|
||||
log.Warn("cache eviction settings are ignored when cache.s3 is set; use lifecycle rules on the bucket instead")
|
||||
logging.Warn("cache eviction settings are ignored when cache.s3 is set; use lifecycle rules on the bucket instead")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -913,7 +911,7 @@ func warnIgnoredCacheSecret(cfg *config.Config) {
|
||||
if cfg.Cache.ExternalSecretFile != "" {
|
||||
key = "cache.external_secret_file"
|
||||
}
|
||||
log.Warnf("%s is set but cache.external_server is not; the built-in cache server does not use a shared secret, so the value is ignored", key)
|
||||
logging.Warnf("%s is set but cache.external_server is not; the built-in cache server does not use a shared secret, so the value is ignored", key)
|
||||
}
|
||||
|
||||
// cacheForJob returns the cache address for the job, and the container to attach to its network to reach it there.
|
||||
@@ -943,25 +941,26 @@ func (r *Runner) detectIsolatedCacheContainer() string {
|
||||
defer cancel()
|
||||
cacheContainer, err := container.IsolatedCacheContainer(ctx, addr, r.cfg.Container.Network)
|
||||
if err != nil {
|
||||
log.Warnf("cannot check whether jobs reach the cache server: %v", err)
|
||||
logging.Warnf("cannot check whether jobs reach the cache server: %v", err)
|
||||
}
|
||||
return cacheContainer
|
||||
}
|
||||
|
||||
func taskLogFields(task *runnerv1.Task, job string) log.Fields {
|
||||
// taskLogFields returns key/value pairs for logging.With.
|
||||
func taskLogFields(task *runnerv1.Task, job string) []any {
|
||||
taskContext := task.GetContext().GetFields()
|
||||
fields := log.Fields{
|
||||
"task_id": task.Id,
|
||||
"job": job,
|
||||
"repository": taskContext["repository"].GetStringValue(),
|
||||
"run_id": taskContext["run_id"].GetStringValue(),
|
||||
"run_number": taskContext["run_number"].GetStringValue(),
|
||||
fields := []any{
|
||||
"task_id", task.Id,
|
||||
"job", job,
|
||||
"repository", taskContext["repository"].GetStringValue(),
|
||||
"run_id", taskContext["run_id"].GetStringValue(),
|
||||
"run_number", taskContext["run_number"].GetStringValue(),
|
||||
}
|
||||
if parts := strings.Split(taskContext["gitea_runtime_token"].GetStringValue(), "."); len(parts) == 3 {
|
||||
var claims struct{ JobID int64 } // Gitea's actionsClaims fields are untagged, so the claim key is "JobID"
|
||||
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
|
||||
if err == nil && json.Unmarshal(payload, &claims) == nil && claims.JobID > 0 {
|
||||
fields["job_id"] = claims.JobID
|
||||
fields = append(fields, "job_id", claims.JobID)
|
||||
}
|
||||
}
|
||||
return fields
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
"connectrpc.com/connect"
|
||||
"gitea.dev/actionslib/pkg/model"
|
||||
runnerv1 "gitea.dev/actionslib/runner/v1"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -418,13 +417,12 @@ func TestRestoreCalledWorkflowContext(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTaskLogFields(t *testing.T) {
|
||||
fields := func(token string) log.Fields {
|
||||
fields := func(token string) []any {
|
||||
taskContext, err := structpb.NewStruct(map[string]any{"repository": "owner/repo", "run_id": "159", "run_number": "14", "gitea_runtime_token": token})
|
||||
require.NoError(t, err)
|
||||
return taskLogFields(&runnerv1.Task{Id: 72, Context: taskContext}, "build")
|
||||
}
|
||||
want := log.Fields{"task_id": int64(72), "job": "build", "repository": "owner/repo", "run_id": "159", "run_number": "14"}
|
||||
want := []any{"task_id", int64(72), "job", "build", "repository", "owner/repo", "run_id", "159", "run_number", "14"}
|
||||
assert.Equal(t, want, fields("malformed"))
|
||||
want["job_id"] = int64(221)
|
||||
assert.Equal(t, want, fields("header."+base64.RawURLEncoding.EncodeToString([]byte(`{"TaskID":72,"JobID":221}`))+".signature"))
|
||||
assert.Equal(t, append(want, "job_id", int64(221)), fields("header."+base64.RawURLEncoding.EncodeToString([]byte(`{"TaskID":72,"JobID":221}`))+".signature"))
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# each task's log kept under log.job. Neither controls how workflow step output is streamed
|
||||
# to the Gitea UI; tune that with runner.log_report_* below.
|
||||
log:
|
||||
# logrus severity: trace, debug, info, warn, error, fatal, panic.
|
||||
# Severity: trace, debug, info, warn, error. fatal and panic are accepted and mean error.
|
||||
# trace and debug turn on caller/file:line in log lines. Default if omitted: info.
|
||||
#level: info
|
||||
# Write a copy of each task's log to dir as <start time>-task-<id>.log, so a job's output
|
||||
|
||||
@@ -15,9 +15,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.com/gitea/runner/act/common/logging"
|
||||
|
||||
"github.com/docker/go-units"
|
||||
"github.com/joho/godotenv"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.yaml.in/yaml/v4"
|
||||
"golang.org/x/net/http/httpguts"
|
||||
)
|
||||
@@ -408,26 +409,26 @@ func LoadDefault(file string) (*Config, error) {
|
||||
|
||||
// Validate and fix invalid config combinations to prevent confusing behavior.
|
||||
if cfg.Runner.ToolCacheMode == ToolCacheModeShared && cfg.Runner.Capacity > 1 {
|
||||
log.Warnf("runner.tool_cache_mode %q with capacity %d: two jobs writing the same tool version at once corrupt it",
|
||||
logging.Warnf("runner.tool_cache_mode %q with capacity %d: two jobs writing the same tool version at once corrupt it",
|
||||
ToolCacheModeShared, cfg.Runner.Capacity)
|
||||
}
|
||||
if cfg.Runner.FetchTimeout > RequestTimeout {
|
||||
log.Warnf("fetch_timeout (%v) exceeds the RPC timeout (%v), capping it", cfg.Runner.FetchTimeout, RequestTimeout)
|
||||
logging.Warnf("fetch_timeout (%v) exceeds the RPC timeout (%v), capping it", cfg.Runner.FetchTimeout, RequestTimeout)
|
||||
cfg.Runner.FetchTimeout = RequestTimeout
|
||||
}
|
||||
if cfg.Runner.FetchIntervalMax < cfg.Runner.FetchInterval {
|
||||
log.Warnf("fetch_interval_max (%v) is less than fetch_interval (%v), setting fetch_interval_max to fetch_interval",
|
||||
logging.Warnf("fetch_interval_max (%v) is less than fetch_interval (%v), setting fetch_interval_max to fetch_interval",
|
||||
cfg.Runner.FetchIntervalMax, cfg.Runner.FetchInterval)
|
||||
cfg.Runner.FetchIntervalMax = cfg.Runner.FetchInterval
|
||||
}
|
||||
if cfg.Runner.LogReportMaxLatency >= cfg.Runner.LogReportInterval {
|
||||
log.Warnf("log_report_max_latency (%v) >= log_report_interval (%v), the max-latency timer will never fire before the periodic ticker; consider lowering log_report_max_latency",
|
||||
logging.Warnf("log_report_max_latency (%v) >= log_report_interval (%v), the max-latency timer will never fire before the periodic ticker; consider lowering log_report_max_latency",
|
||||
cfg.Runner.LogReportMaxLatency, cfg.Runner.LogReportInterval)
|
||||
}
|
||||
|
||||
// although `container.network_mode` will be deprecated, but we have to be compatible with it for now.
|
||||
if cfg.Container.NetworkMode != "" && cfg.Container.Network == "" {
|
||||
log.Warn("You are trying to use deprecated configuration item of `container.network_mode`, please use `container.network` instead.")
|
||||
logging.Warn("You are trying to use deprecated configuration item of `container.network_mode`, please use `container.network` instead.")
|
||||
if cfg.Container.NetworkMode == "bridge" {
|
||||
// Previously, if the value of `container.network_mode` is `bridge`, we will create a new network for job.
|
||||
// But “bridge” is easily confused with the bridge network created by Docker by default.
|
||||
@@ -493,7 +494,7 @@ func warnUnknownKeys(file string, document *yaml.Node) {
|
||||
var loadErrs *yaml.LoadErrors
|
||||
if err := document.Load(&Config{}, yaml.WithV3Defaults(), yaml.WithKnownFields()); errors.As(err, &loadErrs) {
|
||||
for _, loadErr := range loadErrs.Errors {
|
||||
log.Warnf("config file %q: %s, it will be ignored", file, loadErr)
|
||||
logging.Warnf("config file %q: %s, it will be ignored", file, loadErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus/hooks/test"
|
||||
"gitea.com/gitea/runner/act/common/logging/logtest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -391,14 +392,13 @@ func TestLoadDefault_WarnsOnUnknownKeysButStillLoads(t *testing.T) {
|
||||
path := filepath.Join(dir, "config.yaml")
|
||||
require.NoError(t, os.WriteFile(path, []byte("container:\n volumes:\n - /host:/ctr\n privileged: true\n"), 0o600))
|
||||
|
||||
hook := test.NewGlobal()
|
||||
defer hook.Reset()
|
||||
hook := logtest.SetDefault(t)
|
||||
|
||||
cfg, err := LoadDefault(path)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, cfg.Container.Privileged)
|
||||
require.Len(t, hook.Entries, 1)
|
||||
assert.Contains(t, hook.LastEntry().Message, "field volumes not found")
|
||||
require.Len(t, hook.AllRecords(), 1)
|
||||
assert.Contains(t, hook.LastRecord().Message, "field volumes not found")
|
||||
}
|
||||
|
||||
func TestContainerNetworkCreateOptions(t *testing.T) {
|
||||
@@ -539,8 +539,7 @@ cache:
|
||||
// The shipped configs must parse, hold no key the config does not know, and leave
|
||||
// every option at its default, as all of their values are commented out.
|
||||
func TestLoadDefault_ShippedConfigsChangeNothing(t *testing.T) {
|
||||
hook := test.NewGlobal()
|
||||
defer hook.Reset()
|
||||
hook := logtest.SetDefault(t)
|
||||
|
||||
defaults, err := LoadDefault("")
|
||||
require.NoError(t, err)
|
||||
@@ -554,7 +553,7 @@ func TestLoadDefault_ShippedConfigsChangeNothing(t *testing.T) {
|
||||
require.NoError(t, err, name)
|
||||
assert.Equal(t, defaults, cfg, name)
|
||||
}
|
||||
assert.Empty(t, hook.AllEntries())
|
||||
assert.Empty(t, hook.AllRecords())
|
||||
}
|
||||
|
||||
func TestLoadDefault_ClampsFetchTimeout(t *testing.T) {
|
||||
|
||||
@@ -8,8 +8,9 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"gitea.com/gitea/runner/act/common/logging"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// StartServer starts an HTTP server that serves Prometheus metrics on /metrics
|
||||
@@ -26,9 +27,9 @@ func StartServer(ctx context.Context, addr string, readiness func() (bool, strin
|
||||
}
|
||||
|
||||
go func() {
|
||||
log.Infof("metrics server listening on %s", addr)
|
||||
logging.Infof("metrics server listening on %s", addr)
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.WithError(err).Error("metrics server failed")
|
||||
logging.WithError(err).Error("metrics server failed")
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -37,7 +38,7 @@ func StartServer(ctx context.Context, addr string, readiness func() (bool, strin
|
||||
shutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if err := srv.Shutdown(shutCtx); err != nil {
|
||||
log.WithError(err).Warn("metrics server shutdown error")
|
||||
logging.WithError(err).Warn("metrics server shutdown error")
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ import (
|
||||
"sync/atomic"
|
||||
|
||||
"gitea.com/gitea/runner/act/runner"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// A job's plan runs before its job logger exists and logs through the process-wide one, which
|
||||
@@ -47,21 +45,12 @@ func rebuildGlobalReplacer() { // caller holds globalMu
|
||||
globalReplacer.Store(runner.NewSecretReplacer(oldnew))
|
||||
}
|
||||
|
||||
// MaskingFormatter wraps f so a registered value cannot reach the process-wide log, fields included.
|
||||
func MaskingFormatter(f log.Formatter) log.Formatter {
|
||||
return &maskingFormatter{inner: f}
|
||||
}
|
||||
|
||||
type maskingFormatter struct{ inner log.Formatter }
|
||||
|
||||
func (m *maskingFormatter) Format(entry *log.Entry) ([]byte, error) {
|
||||
line, err := m.inner.Format(entry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// MaskGlobal hides every registered value in a formatted line of the process-wide log, fields
|
||||
// included. It is meant for logging.TextOptions.Filter.
|
||||
func MaskGlobal(line []byte) []byte {
|
||||
replacer := globalReplacer.Load()
|
||||
if replacer == nil { // nothing to hide, so an idle daemon pays no copy
|
||||
return line, nil
|
||||
return line
|
||||
}
|
||||
return []byte(replacer.Replace(string(line))), nil
|
||||
return []byte(replacer.Replace(string(line)))
|
||||
}
|
||||
|
||||
@@ -4,19 +4,27 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gitea.com/gitea/runner/act/common/logging"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGlobalMasks(t *testing.T) {
|
||||
formatter := MaskingFormatter(&log.TextFormatter{DisableTimestamp: true})
|
||||
prevOut := logging.Output()
|
||||
t.Cleanup(func() {
|
||||
logging.SetOutput(prevOut)
|
||||
logging.SetTextFormat(logging.TextOptions{})
|
||||
})
|
||||
var out strings.Builder
|
||||
logging.SetOutput(&out)
|
||||
logging.SetTextFormat(logging.TextOptions{DisableTimestamp: true, Filter: MaskGlobal})
|
||||
format := func(job string) string {
|
||||
line, err := formatter.Format(log.WithField("job", job))
|
||||
require.NoError(t, err)
|
||||
return string(line)
|
||||
out.Reset()
|
||||
logging.With("job", job).Info("line")
|
||||
return out.String()
|
||||
}
|
||||
register := func(oldnew ...string) *Reporter {
|
||||
r := &Reporter{oldnew: oldnew}
|
||||
|
||||
@@ -10,9 +10,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.com/gitea/runner/act/common/logging"
|
||||
"gitea.com/gitea/runner/internal/pkg/config"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -37,7 +36,7 @@ func openJobLog(cfg config.LogJob, taskID int64, started time.Time) *jobLog {
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(cfg.Dir, 0o700); err != nil { // repository output, readable by this user only
|
||||
log.Warnf("cannot create job log directory %s: %v", cfg.Dir, err)
|
||||
logging.Warnf("cannot create job log directory %s: %v", cfg.Dir, err)
|
||||
return nil
|
||||
}
|
||||
pruneJobLogs(cfg.Dir, cfg.Retention, started)
|
||||
@@ -45,10 +44,10 @@ func openJobLog(cfg config.LogJob, taskID int64, started time.Time) *jobLog {
|
||||
name := fmt.Sprintf("%s-task-%d.log", started.UTC().Format(jobLogNameLayout), taskID)
|
||||
file, err := os.OpenFile(filepath.Join(cfg.Dir, name), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
|
||||
if err != nil {
|
||||
log.Warnf("cannot create job log: %v", err)
|
||||
logging.Warnf("cannot create job log: %v", err)
|
||||
return nil
|
||||
}
|
||||
log.Infof("writing the log of task %d to %s", taskID, file.Name())
|
||||
logging.Infof("writing the log of task %d to %s", taskID, file.Name())
|
||||
return &jobLog{file: file, max: int64(cfg.MaxSize)}
|
||||
}
|
||||
|
||||
@@ -72,7 +71,7 @@ func (j *jobLog) close(trailer string) {
|
||||
j.closed = true
|
||||
j.line(runnerLine(trailer)) // past the cap on purpose: no trailer means the runner died mid-job
|
||||
if err := j.file.Close(); err != nil {
|
||||
log.Warnf("cannot close %s: %v", j.file.Name(), err)
|
||||
logging.Warnf("cannot close %s: %v", j.file.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +82,7 @@ func (j *jobLog) line(content string) {
|
||||
j.size += int64(n)
|
||||
if err != nil {
|
||||
j.stopped = true // reported once, a failing write is a full disk and retrying floods the log
|
||||
log.Warnf("cannot write %s: %v", j.file.Name(), err)
|
||||
logging.Warnf("cannot write %s: %v", j.file.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +98,7 @@ func pruneJobLogs(root string, retention time.Duration, now time.Time) {
|
||||
}
|
||||
entries, err := os.ReadDir(root)
|
||||
if err != nil {
|
||||
log.Warnf("cannot list job log directory %s: %v", root, err)
|
||||
logging.Warnf("cannot list job log directory %s: %v", root, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -114,7 +113,7 @@ func pruneJobLogs(root string, retention time.Duration, now time.Time) {
|
||||
}
|
||||
name := filepath.Join(root, entry.Name())
|
||||
if err := os.Remove(name); err != nil {
|
||||
log.Warnf("cannot remove expired job log %s: %v", name, err)
|
||||
logging.Warnf("cannot remove expired job log %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"connectrpc.com/connect"
|
||||
runnerv1 "gitea.dev/actionslib/runner/v1"
|
||||
"github.com/avast/retry-go/v5"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
@@ -357,10 +356,10 @@ func (r *Reporter) noteReport(method string, err error) {
|
||||
switch {
|
||||
case err != nil && !r.reportFailing[method]:
|
||||
r.reportFailing[method] = true
|
||||
log.Warnf("%s error: %v, retrying until reconnected", method, err)
|
||||
logging.Warnf("%s error: %v, retrying until reconnected", method, err)
|
||||
case err == nil && r.reportFailing[method]:
|
||||
delete(r.reportFailing, method)
|
||||
log.Infof("%s reconnected", method)
|
||||
logging.Infof("%s reconnected", method)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,17 +496,17 @@ func (r *Reporter) SetOutputs(outputs map[string]string) {
|
||||
}
|
||||
for k, v := range outputs {
|
||||
if l := len(k); l > maxOutputKeyLen {
|
||||
log.Warnf("ignore output %q because the key is too long: %d > %d", k, l, maxOutputKeyLen)
|
||||
logging.Warnf("ignore output %q because the key is too long: %d > %d", k, l, maxOutputKeyLen)
|
||||
r.logf("ignore output %q because the key is too long: %d > %d", k, l, maxOutputKeyLen)
|
||||
continue
|
||||
}
|
||||
if l := len(v); l > maxOutputValueLen {
|
||||
log.Warnf("ignore output %q because the value is too long: %d > %d", k, l, maxOutputValueLen)
|
||||
logging.Warnf("ignore output %q because the value is too long: %d > %d", k, l, maxOutputValueLen)
|
||||
r.logf("ignore output %q because the value is too long: %d > %d", k, l, maxOutputValueLen)
|
||||
continue
|
||||
}
|
||||
if r.logReplacer.Replace(v) != v { // GitHub skips an output that may carry a secret rather than masking it
|
||||
log.Warnf("ignore output %q because it may contain a secret", k)
|
||||
logging.Warnf("ignore output %q because it may contain a secret", k)
|
||||
r.logf("ignore output %q because it may contain a secret", k)
|
||||
continue
|
||||
}
|
||||
@@ -559,7 +558,7 @@ func (r *Reporter) Close(lastWords string) error {
|
||||
select {
|
||||
case <-r.daemon:
|
||||
case <-time.After(r.daemonWait):
|
||||
log.Errorf("No Response from RunDaemon for %s, continue best effort", r.daemonWait)
|
||||
logging.Errorf("No Response from RunDaemon for %s, continue best effort", r.daemonWait)
|
||||
}
|
||||
|
||||
// Gitea's UpdateLog short-circuits on len(Rows)==0 before honoring NoMore,
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"time"
|
||||
|
||||
"gitea.com/gitea/runner/act/common/logging"
|
||||
"gitea.com/gitea/runner/act/common/logging/logtest"
|
||||
"gitea.com/gitea/runner/act/runner"
|
||||
"gitea.com/gitea/runner/internal/pkg/client/mocks"
|
||||
"gitea.com/gitea/runner/internal/pkg/config"
|
||||
@@ -26,7 +27,6 @@ import (
|
||||
connect_go "connectrpc.com/connect"
|
||||
"gitea.dev/actionslib/pkg/model"
|
||||
runnerv1 "gitea.dev/actionslib/runner/v1"
|
||||
logrustest "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -1311,24 +1311,23 @@ func TestReporter_ServerFailureResultIsReportedAsFailure(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReporter_NoteReport(t *testing.T) {
|
||||
hook := logrustest.NewGlobal()
|
||||
defer hook.Reset()
|
||||
hook := logtest.SetDefault(t)
|
||||
|
||||
reporter := &Reporter{reportFailing: map[string]bool{}}
|
||||
|
||||
// A pending output is not a transport failure and must not read as one.
|
||||
reporter.noteReport(metrics.LabelMethodUpdateTask, fmt.Errorf("wrapped: %w", errOutputsNotSent))
|
||||
assert.Empty(t, hook.AllEntries())
|
||||
assert.Empty(t, hook.AllRecords())
|
||||
|
||||
// An outage logs once at each end, not every report interval for hours.
|
||||
reporter.noteReport(metrics.LabelMethodUpdateTask, errors.New("connection refused"))
|
||||
reporter.noteReport(metrics.LabelMethodUpdateTask, errors.New("connection refused"))
|
||||
require.Len(t, hook.AllEntries(), 1)
|
||||
assert.Contains(t, hook.LastEntry().Message, "connection refused")
|
||||
require.Len(t, hook.AllRecords(), 1)
|
||||
assert.Contains(t, hook.LastRecord().Message, "connection refused")
|
||||
|
||||
reporter.noteReport(metrics.LabelMethodUpdateTask, nil)
|
||||
require.Len(t, hook.AllEntries(), 2)
|
||||
assert.Contains(t, hook.LastEntry().Message, "reconnected")
|
||||
require.Len(t, hook.AllRecords(), 2)
|
||||
assert.Contains(t, hook.LastRecord().Message, "reconnected")
|
||||
}
|
||||
|
||||
// A job's final error can carry a secret, e.g. one interpolated into a failing
|
||||
|
||||
@@ -12,12 +12,12 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.com/gitea/runner/act/common/logging"
|
||||
"gitea.com/gitea/runner/internal/pkg/metrics"
|
||||
|
||||
"gitea.dev/actionslib/runner/v1/runnerv1connect"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
commonpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
@@ -61,7 +61,7 @@ func pushMetrics(exporter *client, res *resource.Resource, gatherer prometheus.G
|
||||
return
|
||||
case <-ticker.C:
|
||||
if err := push(ctx); err != nil && ctx.Err() == nil {
|
||||
log.WithError(err).Warn("OTLP metrics export failed, set OTEL_METRICS_EXPORTER=none to disable it")
|
||||
logging.WithError(err).Warn("OTLP metrics export failed, set OTEL_METRICS_EXPORTER=none to disable it")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gitea.com/gitea/runner/act/common/logging"
|
||||
"gitea.com/gitea/runner/internal/pkg/ver"
|
||||
|
||||
"gitea.dev/actionslib/pkg/model"
|
||||
@@ -28,7 +29,6 @@ import (
|
||||
"github.com/avast/retry-go/v5"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
||||
@@ -90,7 +90,7 @@ func Setup(ctx context.Context, uuid, name string, gatherer prometheus.Gatherer)
|
||||
compressionKey := otlpKey(signal, "COMPRESSION")
|
||||
compression := os.Getenv(compressionKey)
|
||||
if !slices.Contains([]string{"", "none", "gzip"}, strings.ToLower(compression)) {
|
||||
log.Warnf("ignoring unsupported %s=%q", compressionKey, compression)
|
||||
logging.Warnf("ignoring unsupported %s=%q", compressionKey, compression)
|
||||
}
|
||||
httpClient, err := newHTTPClient(signal)
|
||||
if err != nil {
|
||||
@@ -144,7 +144,7 @@ func exportsOTLP(signal string) bool {
|
||||
case "none":
|
||||
known = true
|
||||
default:
|
||||
log.Warnf("ignoring unsupported %s value %q", key, value)
|
||||
logging.Warnf("ignoring unsupported %s value %q", key, value)
|
||||
}
|
||||
}
|
||||
return otlp || !known
|
||||
@@ -166,7 +166,7 @@ func millis(key string, fallback, zero time.Duration) time.Duration {
|
||||
}
|
||||
ms, err := strconv.ParseInt(value, 10, 32)
|
||||
if err != nil || ms < 0 {
|
||||
log.Warnf("ignoring %s=%q, it must be a non-negative number of milliseconds", key, value)
|
||||
logging.Warnf("ignoring %s=%q, it must be a non-negative number of milliseconds", key, value)
|
||||
return fallback
|
||||
}
|
||||
return cmp.Or(time.Duration(ms)*time.Millisecond, zero)
|
||||
|
||||
Reference in New Issue
Block a user