From f582ad315921fe24b91402ec27f23957f5a9875b Mon Sep 17 00:00:00 2001 From: qwerty287 <80460567+qwerty287@users.noreply.github.com> Date: Sun, 19 Mar 2023 20:24:43 +0100 Subject: [PATCH] Various enhancements in configuration (#1645) - backends: move to cli flags instead of os.Getenv - ssh: support 2fa with key and password - allow to set grpc jwt secret (solves todo) - allow to set default and max timeout (solves todo) Closes https://github.com/woodpecker-ci/woodpecker/issues/896 Closes https://github.com/woodpecker-ci/woodpecker/issues/1131 --- cli/exec/exec.go | 7 ++- cli/exec/flags.go | 45 ++++++++++++++- cmd/agent/agent.go | 7 ++- cmd/agent/flags.go | 45 ++++++++++++++- cmd/server/flags.go | 18 ++++++ cmd/server/server.go | 4 +- .../30-administration/10-server-config.md | 15 +++++ pipeline/backend/backend.go | 8 +-- pipeline/backend/docker/docker.go | 23 ++++---- pipeline/backend/kubernetes/kubernetes.go | 12 ++-- pipeline/backend/local/local.go | 7 +-- pipeline/backend/ssh/ssh.go | 38 ++++++------- pipeline/backend/types/engine.go | 4 +- pipeline/backend/types/errors.go | 5 ++ pipeline/rpc/proto/woodpecker.pb.go | 57 ++++++++++--------- server/api/repo.go | 16 ++---- server/config.go | 2 + server/forge/mocks/forge.go | 4 +- server/store/mocks/store.go | 8 +-- 19 files changed, 221 insertions(+), 104 deletions(-) create mode 100644 pipeline/backend/types/errors.go diff --git a/cli/exec/exec.go b/cli/exec/exec.go index 9c7bd4852..10b31d097 100644 --- a/cli/exec/exec.go +++ b/cli/exec/exec.go @@ -203,14 +203,15 @@ func execWithAxis(c *cli.Context, file, repoPath string, axis matrix.Axis) error return err } - backend.Init(context.WithValue(c.Context, types.CliContext, c)) + backendCtx := context.WithValue(c.Context, types.CliContext, c) + backend.Init(backendCtx) - engine, err := backend.FindEngine(c.String("backend-engine")) + engine, err := backend.FindEngine(backendCtx, c.String("backend-engine")) if err != nil { return err } - if err = engine.Load(); err != nil { + if err = engine.Load(backendCtx); err != nil { return err } diff --git a/cli/exec/flags.go b/cli/exec/flags.go index 9d5d52cdb..3a8b42bbc 100644 --- a/cli/exec/flags.go +++ b/cli/exec/flags.go @@ -268,7 +268,50 @@ var flags = []cli.Flag{ Name: "env", }, - // TODO: add flags of backends + // backend docker + &cli.BoolFlag{ + EnvVars: []string{"WOODPECKER_BACKEND_DOCKER_ENABLE_IPV6"}, + Name: "backend-docker-ipv6", + Usage: "backend docker enable IPV6", + Value: false, + }, + &cli.StringFlag{ + EnvVars: []string{"WOODPECKER_BACKEND_DOCKER_NETWORK"}, + Name: "backend-docker-network", + Usage: "backend docker network", + }, + &cli.StringFlag{ + EnvVars: []string{"WOODPECKER_BACKEND_DOCKER_VOLUMES"}, + Name: "backend-docker-volumes", + Usage: "backend docker volumes (comma separated)", + }, + + // backend ssh + &cli.StringFlag{ + EnvVars: []string{"WOODPECKER_BACKEND_SSH_ADDRESS"}, + Name: "backend-ssh-address", + Usage: "backend ssh address", + }, + &cli.StringFlag{ + EnvVars: []string{"WOODPECKER_BACKEND_SSH_USER"}, + Name: "backend-ssh-user", + Usage: "backend ssh user", + }, + &cli.StringFlag{ + EnvVars: []string{"WOODPECKER_BACKEND_SSH_KEY"}, + Name: "backend-ssh-key", + Usage: "backend ssh key file", + }, + &cli.StringFlag{ + EnvVars: []string{"WOODPECKER_BACKEND_SSH_KEY_PASSWORD"}, + Name: "backend-ssh-key-password", + Usage: "backend ssh key password", + }, + &cli.StringFlag{ + EnvVars: []string{"WOODPECKER_BACKEND_SSH_PASSWORD"}, + Name: "backend-ssh-password", + Usage: "backend ssh password", + }, // backend k8s &cli.StringFlag{ diff --git a/cmd/agent/agent.go b/cmd/agent/agent.go index 36b119ecb..e04fe471c 100644 --- a/cmd/agent/agent.go +++ b/cmd/agent/agent.go @@ -154,14 +154,15 @@ func loop(c *cli.Context) error { sigterm.Set() }) - backend.Init(context.WithValue(ctx, types.CliContext, c)) + backendCtx := context.WithValue(ctx, types.CliContext, c) + backend.Init(backendCtx) var wg sync.WaitGroup parallel := c.Int("max-workflows") wg.Add(parallel) // new engine - engine, err := backend.FindEngine(c.String("backend-engine")) + engine, err := backend.FindEngine(backendCtx, c.String("backend-engine")) if err != nil { log.Error().Err(err).Msgf("cannot find backend engine '%s'", c.String("backend-engine")) return err @@ -195,7 +196,7 @@ func loop(c *cli.Context) error { defer wg.Done() // load engine (e.g. init api client) - err = engine.Load() + err = engine.Load(backendCtx) if err != nil { log.Error().Err(err).Msg("cannot load backend engine") return diff --git a/cmd/agent/flags.go b/cmd/agent/flags.go index 9f9b6bf97..ba40bd881 100644 --- a/cmd/agent/flags.go +++ b/cmd/agent/flags.go @@ -108,7 +108,50 @@ var flags = []cli.Flag{ Value: "auto-detect", }, - // TODO: add flags of backends + // backend docker + &cli.BoolFlag{ + EnvVars: []string{"WOODPECKER_BACKEND_DOCKER_ENABLE_IPV6"}, + Name: "backend-docker-ipv6", + Usage: "backend docker enable IPV6", + Value: false, + }, + &cli.StringFlag{ + EnvVars: []string{"WOODPECKER_BACKEND_DOCKER_NETWORK"}, + Name: "backend-docker-network", + Usage: "backend docker network", + }, + &cli.StringFlag{ + EnvVars: []string{"WOODPECKER_BACKEND_DOCKER_VOLUMES"}, + Name: "backend-docker-volumes", + Usage: "backend docker volumes (comma separated)", + }, + + // backend ssh + &cli.StringFlag{ + EnvVars: []string{"WOODPECKER_BACKEND_SSH_ADDRESS"}, + Name: "backend-ssh-address", + Usage: "backend ssh address", + }, + &cli.StringFlag{ + EnvVars: []string{"WOODPECKER_BACKEND_SSH_USER"}, + Name: "backend-ssh-user", + Usage: "backend ssh user", + }, + &cli.StringFlag{ + EnvVars: []string{"WOODPECKER_BACKEND_SSH_KEY"}, + Name: "backend-ssh-key", + Usage: "backend ssh key file", + }, + &cli.StringFlag{ + EnvVars: []string{"WOODPECKER_BACKEND_SSH_KEY_PASSWORD"}, + Name: "backend-ssh-key-password", + Usage: "backend ssh key password", + }, + &cli.StringFlag{ + EnvVars: []string{"WOODPECKER_BACKEND_SSH_PASSWORD"}, + Name: "backend-ssh-password", + Usage: "backend ssh password", + }, // backend k8s &cli.StringFlag{ diff --git a/cmd/server/flags.go b/cmd/server/flags.go index 4d0df5226..c6d96f8d1 100644 --- a/cmd/server/flags.go +++ b/cmd/server/flags.go @@ -77,6 +77,12 @@ var flags = []cli.Flag{ Usage: "grpc address", Value: ":9000", }, + &cli.StringFlag{ + EnvVars: []string{"WOODPECKER_GRPC_SECRET"}, + Name: "grpc-secret", + Usage: "grpc jwt secret", + Value: "secret", + }, &cli.StringFlag{ EnvVars: []string{"WOODPECKER_METRICS_SERVER_ADDR"}, Name: "metrics-server-addr", @@ -120,6 +126,18 @@ var flags = []cli.Flag{ Usage: "The default docker image to be used when cloning the repo", Value: constant.DefaultCloneImage, }, + &cli.Int64Flag{ + EnvVars: []string{"WOODPECKER_DEFAULT_PIPELINE_TIMEOUT"}, + Name: "default-pipeline-timeout", + Usage: "The default time in minutes for a repo in minutes before a pipeline gets killed", + Value: 60, + }, + &cli.Int64Flag{ + EnvVars: []string{"WOODPECKER_MAX_PIPELINE_TIMEOUT"}, + Name: "max-pipeline-timeout", + Usage: "The maximum time in minutes you can set in the repo settings before a pipeline gets killed", + Value: 120, + }, &cli.StringFlag{ EnvVars: []string{"WOODPECKER_DOCS"}, Name: "docs", diff --git a/cmd/server/server.go b/cmd/server/server.go index 236da57eb..9ba955c53 100644 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -135,7 +135,7 @@ func run(c *cli.Context) error { return err } - jwtSecret := "secret" // TODO: make configurable + jwtSecret := c.String("grpc-secret") jwtManager := woodpeckerGrpcServer.NewJWTManager(jwtSecret) authorizer := woodpeckerGrpcServer.NewAuthorizer(jwtManager) @@ -321,6 +321,8 @@ func setupEvilGlobals(c *cli.Context, v store.Store, f forge.Forge) { events = append(events, model.WebhookEvent(v)) } server.Config.Pipeline.DefaultCancelPreviousPipelineEvents = events + server.Config.Pipeline.DefaultTimeout = c.Int64("default-pipeline-timeout") + server.Config.Pipeline.MaxTimeout = c.Int64("max-pipeline-timeout") // limits server.Config.Pipeline.Limits.MemSwapLimit = c.Int64("limit-mem-swap") diff --git a/docs/docs/30-administration/10-server-config.md b/docs/docs/30-administration/10-server-config.md index 6407e67ba..e594b52e2 100644 --- a/docs/docs/30-administration/10-server-config.md +++ b/docs/docs/30-administration/10-server-config.md @@ -160,6 +160,11 @@ Automatically generates an SSL certificate using Let's Encrypt, and configures t Configures the gRPC listener port. +### `WOODPECKER_GRPC_SECRET` +> Default: `secret` + +Configures the gRPC JWT secret. + ### `WOODPECKER_METRICS_SERVER_ADDR` > Default: empty @@ -213,6 +218,16 @@ List of event names that will be canceled when a new pipeline for the same conte The default docker image to be used when cloning the repo +### `WOODPECKER_DEFAULT_PIPELINE_TIMEOUT` +> 60 (minutes) + +The default time for a repo in minutes before a pipeline gets killed + +### `WOODPECKER_MAX_PIPELINE_TIMEOUT` +> 120 (minutes) + +The maximum time in minutes you can set in the repo settings before a pipeline gets killed + ### `WOODPECKER_SESSION_EXPIRES` > Default: `72h` diff --git a/pipeline/backend/backend.go b/pipeline/backend/backend.go index 781a83185..2075a3cac 100644 --- a/pipeline/backend/backend.go +++ b/pipeline/backend/backend.go @@ -30,20 +30,20 @@ func Init(ctx context.Context) { } } -func FindEngine(engineName string) (types.Engine, error) { +func FindEngine(ctx context.Context, engineName string) (types.Engine, error) { if engineName == "auto-detect" { for _, engine := range engines { - if engine.IsAvailable() { + if engine.IsAvailable(ctx) { return engine, nil } } - return nil, fmt.Errorf("Can't detect an available backend engine") + return nil, fmt.Errorf("can't detect an available backend engine") } engine, ok := enginesByName[engineName] if !ok { - return nil, fmt.Errorf("Backend engine '%s' not found", engineName) + return nil, fmt.Errorf("backend engine '%s' not found", engineName) } return engine, nil diff --git a/pipeline/backend/docker/docker.go b/pipeline/backend/docker/docker.go index c211aa87c..f93b7f1c1 100644 --- a/pipeline/backend/docker/docker.go +++ b/pipeline/backend/docker/docker.go @@ -18,7 +18,6 @@ import ( "context" "io" "os" - "strconv" "strings" "github.com/docker/docker/api/types" @@ -29,6 +28,7 @@ import ( "github.com/moby/moby/pkg/stdcopy" "github.com/moby/term" "github.com/rs/zerolog/log" + "github.com/urfave/cli/v2" backend "github.com/woodpecker-ci/woodpecker/pipeline/backend/types" "github.com/woodpecker-ci/woodpecker/shared/utils" @@ -41,9 +41,6 @@ type docker struct { volumes []string } -// make sure docker implements Engine -var _ backend.Engine = &docker{} - // New returns a new Docker Engine. func New() backend.Engine { return &docker{ @@ -55,7 +52,7 @@ func (e *docker) Name() string { return "docker" } -func (e *docker) IsAvailable() bool { +func (e *docker) IsAvailable(context.Context) bool { if os.Getenv("DOCKER_HOST") != "" { return true } @@ -64,18 +61,22 @@ func (e *docker) IsAvailable() bool { } // Load new client for Docker Engine using environment variables. -func (e *docker) Load() error { - cli, err := client.NewClientWithOpts(client.FromEnv) +func (e *docker) Load(ctx context.Context) error { + cl, err := client.NewClientWithOpts(client.FromEnv) if err != nil { return err } - e.client = cli + e.client = cl - e.enableIPv6, _ = strconv.ParseBool(os.Getenv("WOODPECKER_BACKEND_DOCKER_ENABLE_IPV6")) + c, ok := ctx.Value(backend.CliContext).(*cli.Context) + if !ok { + return backend.ErrNoCliContextFound + } + e.enableIPv6 = c.Bool("backend-docker-ipv6") - e.network = os.Getenv("WOODPECKER_BACKEND_DOCKER_NETWORK") + e.network = c.String("backend-docker-network") - volumes := strings.Split(os.Getenv("WOODPECKER_BACKEND_DOCKER_VOLUMES"), ",") + volumes := strings.Split(c.String("backend-docker-volumes"), ",") e.volumes = make([]string, 0, len(volumes)) // Validate provided volume definitions for _, v := range volumes { diff --git a/pipeline/backend/kubernetes/kubernetes.go b/pipeline/backend/kubernetes/kubernetes.go index 1d9529625..f22353db8 100644 --- a/pipeline/backend/kubernetes/kubernetes.go +++ b/pipeline/backend/kubernetes/kubernetes.go @@ -2,7 +2,6 @@ package kubernetes import ( "context" - std_errors "errors" "fmt" "io" "os" @@ -27,10 +26,7 @@ import ( _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" ) -var ( - ErrNoCliContextFound = std_errors.New("No CliContext in context found.") - noContext = context.Background() -) +var noContext = context.Background() type kube struct { ctx context.Context @@ -75,7 +71,7 @@ func configFromCliContext(ctx context.Context) (*Config, error) { } } - return nil, ErrNoCliContextFound + return nil, types.ErrNoCliContextFound } // New returns a new Kubernetes Engine. @@ -89,12 +85,12 @@ func (e *kube) Name() string { return "kubernetes" } -func (e *kube) IsAvailable() bool { +func (e *kube) IsAvailable(context.Context) bool { host := os.Getenv("KUBERNETES_SERVICE_HOST") return len(host) > 0 } -func (e *kube) Load() error { +func (e *kube) Load(context.Context) error { config, err := configFromCliContext(e.ctx) if err != nil { return err diff --git a/pipeline/backend/local/local.go b/pipeline/backend/local/local.go index f3efeb409..bfc99b35d 100644 --- a/pipeline/backend/local/local.go +++ b/pipeline/backend/local/local.go @@ -47,9 +47,6 @@ type local struct { workingdir string } -// make sure local implements Engine -var _ types.Engine = &local{} - // New returns a new local Engine. func New() types.Engine { return &local{} @@ -59,11 +56,11 @@ func (e *local) Name() string { return "local" } -func (e *local) IsAvailable() bool { +func (e *local) IsAvailable(context.Context) bool { return true } -func (e *local) Load() error { +func (e *local) Load(context.Context) error { dir, err := os.MkdirTemp("", "woodpecker-local-*") e.workingdir = dir return err diff --git a/pipeline/backend/ssh/ssh.go b/pipeline/backend/ssh/ssh.go index 376d3838b..5e6c96efb 100644 --- a/pipeline/backend/ssh/ssh.go +++ b/pipeline/backend/ssh/ssh.go @@ -2,12 +2,11 @@ package ssh import ( "context" - "fmt" "io" - "os" "strings" "github.com/melbahja/goph" + "github.com/urfave/cli/v2" "github.com/woodpecker-ci/woodpecker/pipeline/backend/common" "github.com/woodpecker-ci/woodpecker/pipeline/backend/types" @@ -29,9 +28,6 @@ func (c readCloser) Close() error { return nil } -// make sure local implements Engine -var _ types.Engine = &ssh{} - // New returns a new ssh Engine. func New() types.Engine { return &ssh{} @@ -41,11 +37,12 @@ func (e *ssh) Name() string { return "ssh" } -func (e *ssh) IsAvailable() bool { - return os.Getenv("WOODPECKER_BACKEND_SSH_KEY") != "" || os.Getenv("WOODPECKER_BACKEND_SSH_PASSWORD") != "" +func (e *ssh) IsAvailable(ctx context.Context) bool { + c, ok := ctx.Value(types.CliContext).(*cli.Context) + return ok && c.String("backend-ssh-address") != "" && c.String("backend-ssh-user") != "" && (c.String("backend-ssh-key") != "" || c.String("backend-ssh-password") != "") } -func (e *ssh) Load() error { +func (e *ssh) Load(ctx context.Context) error { cmd, err := e.client.Command("/bin/env", "mktemp", "-d", "-p", "/tmp", "woodpecker-ssh-XXXXXXXXXX") if err != nil { return err @@ -57,23 +54,22 @@ func (e *ssh) Load() error { } e.workingdir = string(dir) - address := os.Getenv("WOODPECKER_BACKEND_SSH_ADDRESS") - if address == "" { - return fmt.Errorf("missing SSH address") - } - user := os.Getenv("WOODPECKER_BACKEND_SSH_USER") - if user == "" { - return fmt.Errorf("missing SSH user") + c, ok := ctx.Value(types.CliContext).(*cli.Context) + if !ok { + return types.ErrNoCliContextFound } + address := c.String("backend-ssh-address") + user := c.String("backend-ssh-user") var auth goph.Auth - if file, has := os.LookupEnv("WOODPECKER_BACKEND_SSH_KEY"); has { - var err error - auth, err = goph.Key(file, os.Getenv("WOODPECKER_BACKEND_SSH_KEY_PASSWORD")) + if file := c.String("backend-ssh-key"); file != "" { + keyAuth, err := goph.Key(file, c.String("backend-ssh-key-password")) if err != nil { return err } - } else { - auth = goph.Password(os.Getenv("WOODPECKER_BACKEND_SSH_PASSWORD")) + auth = append(auth, keyAuth...) + } + if password := c.String("backend-ssh-password"); password != "" { + auth = append(auth, goph.Password(password)...) } client, err := goph.New(user, address, auth) if err != nil { @@ -91,7 +87,7 @@ func (e *ssh) Setup(_ context.Context, _ *types.Config) error { // Exec the pipeline step. func (e *ssh) Exec(ctx context.Context, step *types.Step) error { // Get environment variables - command := []string{} + var command []string for a, b := range step.Environment { if a != "HOME" && a != "SHELL" { // Don't override $HOME and $SHELL command = append(command, a+"="+b) diff --git a/pipeline/backend/types/engine.go b/pipeline/backend/types/engine.go index 8a5984ead..914a218e0 100644 --- a/pipeline/backend/types/engine.go +++ b/pipeline/backend/types/engine.go @@ -9,8 +9,8 @@ import ( // to create and manage container resources. type Engine interface { Name() string - IsAvailable() bool - Load() error + IsAvailable(context.Context) bool + Load(context.Context) error // Setup the pipeline environment. Setup(context.Context, *Config) error diff --git a/pipeline/backend/types/errors.go b/pipeline/backend/types/errors.go new file mode 100644 index 000000000..8225b62e3 --- /dev/null +++ b/pipeline/backend/types/errors.go @@ -0,0 +1,5 @@ +package types + +import "errors" + +var ErrNoCliContextFound = errors.New("no CliContext in context found") diff --git a/pipeline/rpc/proto/woodpecker.pb.go b/pipeline/rpc/proto/woodpecker.pb.go index f3b8b2814..e716db976 100644 --- a/pipeline/rpc/proto/woodpecker.pb.go +++ b/pipeline/rpc/proto/woodpecker.pb.go @@ -7,10 +7,11 @@ package proto import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( @@ -1326,31 +1327,33 @@ func file_woodpecker_proto_rawDescGZIP() []byte { return file_woodpecker_proto_rawDescData } -var file_woodpecker_proto_msgTypes = make([]protoimpl.MessageInfo, 22) -var file_woodpecker_proto_goTypes = []interface{}{ - (*File)(nil), // 0: proto.File - (*State)(nil), // 1: proto.State - (*Line)(nil), // 2: proto.Line - (*Filter)(nil), // 3: proto.Filter - (*Pipeline)(nil), // 4: proto.Pipeline - (*NextRequest)(nil), // 5: proto.NextRequest - (*NextReply)(nil), // 6: proto.NextReply - (*InitRequest)(nil), // 7: proto.InitRequest - (*WaitRequest)(nil), // 8: proto.WaitRequest - (*DoneRequest)(nil), // 9: proto.DoneRequest - (*ExtendRequest)(nil), // 10: proto.ExtendRequest - (*UploadRequest)(nil), // 11: proto.UploadRequest - (*UpdateRequest)(nil), // 12: proto.UpdateRequest - (*LogRequest)(nil), // 13: proto.LogRequest - (*Empty)(nil), // 14: proto.Empty - (*ReportHealthRequest)(nil), // 15: proto.ReportHealthRequest - (*RegisterAgentRequest)(nil), // 16: proto.RegisterAgentRequest - (*RegisterAgentResponse)(nil), // 17: proto.RegisterAgentResponse - (*AuthRequest)(nil), // 18: proto.AuthRequest - (*AuthReply)(nil), // 19: proto.AuthReply - nil, // 20: proto.File.MetaEntry - nil, // 21: proto.Filter.LabelsEntry -} +var ( + file_woodpecker_proto_msgTypes = make([]protoimpl.MessageInfo, 22) + file_woodpecker_proto_goTypes = []interface{}{ + (*File)(nil), // 0: proto.File + (*State)(nil), // 1: proto.State + (*Line)(nil), // 2: proto.Line + (*Filter)(nil), // 3: proto.Filter + (*Pipeline)(nil), // 4: proto.Pipeline + (*NextRequest)(nil), // 5: proto.NextRequest + (*NextReply)(nil), // 6: proto.NextReply + (*InitRequest)(nil), // 7: proto.InitRequest + (*WaitRequest)(nil), // 8: proto.WaitRequest + (*DoneRequest)(nil), // 9: proto.DoneRequest + (*ExtendRequest)(nil), // 10: proto.ExtendRequest + (*UploadRequest)(nil), // 11: proto.UploadRequest + (*UpdateRequest)(nil), // 12: proto.UpdateRequest + (*LogRequest)(nil), // 13: proto.LogRequest + (*Empty)(nil), // 14: proto.Empty + (*ReportHealthRequest)(nil), // 15: proto.ReportHealthRequest + (*RegisterAgentRequest)(nil), // 16: proto.RegisterAgentRequest + (*RegisterAgentResponse)(nil), // 17: proto.RegisterAgentResponse + (*AuthRequest)(nil), // 18: proto.AuthRequest + (*AuthReply)(nil), // 19: proto.AuthReply + nil, // 20: proto.File.MetaEntry + nil, // 21: proto.Filter.LabelsEntry + } +) var file_woodpecker_proto_depIdxs = []int32{ 20, // 0: proto.File.meta:type_name -> proto.File.MetaEntry 21, // 1: proto.Filter.labels:type_name -> proto.Filter.LabelsEntry diff --git a/server/api/repo.go b/server/api/repo.go index 394215e20..e04b0ed7c 100644 --- a/server/api/repo.go +++ b/server/api/repo.go @@ -32,12 +32,6 @@ import ( "github.com/woodpecker-ci/woodpecker/shared/token" ) -// TODO: make it set system wide via environment variables -const ( - defaultTimeout int64 = 60 // 1 hour default pipeline time - maxTimeout int64 = defaultTimeout * 2 -) - func PostRepo(c *gin.Context) { forge := server.Config.Services.Forge _store := store.FromContext(c) @@ -62,9 +56,9 @@ func PostRepo(c *gin.Context) { } if repo.Timeout == 0 { - repo.Timeout = defaultTimeout - } else if repo.Timeout > maxTimeout { - repo.Timeout = maxTimeout + repo.Timeout = server.Config.Pipeline.DefaultTimeout + } else if repo.Timeout > server.Config.Pipeline.MaxTimeout { + repo.Timeout = server.Config.Pipeline.MaxTimeout } if repo.Hash == "" { @@ -126,8 +120,8 @@ func PatchRepo(c *gin.Context) { return } - if in.Timeout != nil && *in.Timeout > maxTimeout && !user.Admin { - c.String(http.StatusForbidden, fmt.Sprintf("Timeout is not allowed to be higher than max timeout (%dmin)", maxTimeout)) + if in.Timeout != nil && *in.Timeout > server.Config.Pipeline.MaxTimeout && !user.Admin { + c.String(http.StatusForbidden, fmt.Sprintf("Timeout is not allowed to be higher than max timeout (%dmin)", server.Config.Pipeline.MaxTimeout)) } if in.IsTrusted != nil && *in.IsTrusted != repo.IsTrusted && !user.Admin { log.Trace().Msgf("user '%s' wants to make repo trusted without being an instance admin ", user.Login) diff --git a/server/config.go b/server/config.go index 7bb7f1205..113e4f954 100644 --- a/server/config.go +++ b/server/config.go @@ -81,6 +81,8 @@ var Config = struct { Volumes []string Networks []string Privileged []string + DefaultTimeout int64 + MaxTimeout int64 } FlatPermissions bool // TODO(485) temporary workaround to not hit api rate limits }{} diff --git a/server/forge/mocks/forge.go b/server/forge/mocks/forge.go index a684d3366..140b0b0a0 100644 --- a/server/forge/mocks/forge.go +++ b/server/forge/mocks/forge.go @@ -34,7 +34,7 @@ func (_m *Forge) Activate(ctx context.Context, u *model.User, r *model.Repo, lin } // Auth provides a mock function with given fields: ctx, token, secret -func (_m *Forge) Auth(ctx context.Context, token string, secret string) (string, error) { +func (_m *Forge) Auth(ctx context.Context, token, secret string) (string, error) { ret := _m.Called(ctx, token, secret) var r0 string @@ -353,7 +353,7 @@ func (_m *Forge) PullRequests(ctx context.Context, u *model.User, r *model.Repo, } // Repo provides a mock function with given fields: ctx, u, remoteID, owner, name -func (_m *Forge) Repo(ctx context.Context, u *model.User, remoteID model.ForgeRemoteID, owner string, name string) (*model.Repo, error) { +func (_m *Forge) Repo(ctx context.Context, u *model.User, remoteID model.ForgeRemoteID, owner, name string) (*model.Repo, error) { ret := _m.Called(ctx, u, remoteID, owner, name) var r0 *model.Repo diff --git a/server/store/mocks/store.go b/server/store/mocks/store.go index 82d513e2f..00cd435da 100644 --- a/server/store/mocks/store.go +++ b/server/store/mocks/store.go @@ -406,7 +406,7 @@ func (_m *Store) CronList(_a0 *model.Repo) ([]*model.Cron, error) { } // CronListNextExecute provides a mock function with given fields: _a0, _a1 -func (_m *Store) CronListNextExecute(_a0 int64, _a1 int64) ([]*model.Cron, error) { +func (_m *Store) CronListNextExecute(_a0, _a1 int64) ([]*model.Cron, error) { ret := _m.Called(_a0, _a1) var r0 []*model.Cron @@ -618,7 +618,7 @@ func (_m *Store) GetPipeline(_a0 int64) (*model.Pipeline, error) { } // GetPipelineCommit provides a mock function with given fields: _a0, _a1, _a2 -func (_m *Store) GetPipelineCommit(_a0 *model.Repo, _a1 string, _a2 string) (*model.Pipeline, error) { +func (_m *Store) GetPipelineCommit(_a0 *model.Repo, _a1, _a2 string) (*model.Pipeline, error) { ret := _m.Called(_a0, _a1, _a2) var r0 *model.Pipeline @@ -1210,7 +1210,7 @@ func (_m *Store) Migrate() error { } // OrgSecretFind provides a mock function with given fields: _a0, _a1 -func (_m *Store) OrgSecretFind(_a0 string, _a1 string) (*model.Secret, error) { +func (_m *Store) OrgSecretFind(_a0, _a1 string) (*model.Secret, error) { ret := _m.Called(_a0, _a1) var r0 *model.Secret @@ -1676,7 +1676,7 @@ func (_m *Store) ServerConfigGet(_a0 string) (string, error) { } // ServerConfigSet provides a mock function with given fields: _a0, _a1 -func (_m *Store) ServerConfigSet(_a0 string, _a1 string) error { +func (_m *Store) ServerConfigSet(_a0, _a1 string) error { ret := _m.Called(_a0, _a1) var r0 error