diff --git a/cmd/server/flags.go b/cmd/server/flags.go index 9789a8089..b10a3a3b7 100644 --- a/cmd/server/flags.go +++ b/cmd/server/flags.go @@ -273,6 +273,12 @@ var flags = append([]cli.Flag{ Usage: "how many seconds before timeout when fetching the Woodpecker configuration from a Forge", Value: time.Second * 3, }, + &cli.UintFlag{ + EnvVars: []string{"WOODPECKER_FORGE_RETRY"}, + Name: "forge-retry", + Usage: "How many retries of fetching the Woodpecker configuration from a forge are done before we fail", + Value: 3, + }, &cli.Int64Flag{ EnvVars: []string{"WOODPECKER_LIMIT_MEM_SWAP"}, Name: "limit-mem-swap", diff --git a/docs/docs/30-administration/10-server-config.md b/docs/docs/30-administration/10-server-config.md index 70a262c85..8bd7d49c2 100644 --- a/docs/docs/30-administration/10-server-config.md +++ b/docs/docs/30-administration/10-server-config.md @@ -525,6 +525,12 @@ Specify a configuration service endpoint, see [Configuration Extension](./100-ex Specify timeout when fetching the Woodpecker configuration from forge. See for syntax reference. +### `WOODPECKER_FORGE_RETRY` + +> Default: 3 + +Specify how many retries of fetching the Woodpecker configuration from a forge are done before we fail. + ### `WOODPECKER_ENABLE_SWAGGER` > Default: true diff --git a/server/services/config/combined_test.go b/server/services/config/combined_test.go index 4bccd6d50..d2a767cac 100644 --- a/server/services/config/combined_test.go +++ b/server/services/config/combined_test.go @@ -221,7 +221,7 @@ func TestFetchFromConfigService(t *testing.T) { f.On("Netrc", mock.Anything, mock.Anything).Return(&model.Netrc{Machine: "mock", Login: "mock", Password: "mock"}, nil) - forgeFetcher := config.NewForge(time.Second * 3) + forgeFetcher := config.NewForge(time.Second*3, 3) configFetcher := config.NewCombined(forgeFetcher, httpFetcher) files, err := configFetcher.Fetch( context.Background(), diff --git a/server/services/config/forge.go b/server/services/config/forge.go index 9de4bfb3f..a979f5b4d 100644 --- a/server/services/config/forge.go +++ b/server/services/config/forge.go @@ -29,17 +29,15 @@ import ( "go.woodpecker-ci.org/woodpecker/v2/shared/constant" ) -const ( - forgeFetchingRetryCount = 3 -) - type forgeFetcher struct { - timeout time.Duration + timeout time.Duration + retryCount uint } -func NewForge(timeout time.Duration) Service { +func NewForge(timeout time.Duration, retries uint) Service { return &forgeFetcher{ - timeout: timeout, + timeout: timeout, + retryCount: retries, } } @@ -58,7 +56,7 @@ func (f *forgeFetcher) Fetch(ctx context.Context, forge forge.Forge, user *model } // try to fetch multiple times - for i := 0; i < forgeFetchingRetryCount; i++ { + for i := 0; i < int(f.retryCount); i++ { files, err = ffc.fetch(ctx, strings.TrimSpace(repo.Config)) if err != nil { log.Trace().Err(err).Msgf("%d. try failed", i+1) diff --git a/server/services/config/forge_test.go b/server/services/config/forge_test.go index c442e78fd..6a8651203 100644 --- a/server/services/config/forge_test.go +++ b/server/services/config/forge_test.go @@ -306,7 +306,8 @@ func TestFetch(t *testing.T) { f.On("Dir", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("directory not found")) configFetcher := config.NewForge( - time.Second * 3, + time.Second*3, + 3, ) files, err := configFetcher.Fetch( context.Background(), diff --git a/server/services/manager.go b/server/services/manager.go index ff281c40a..ce0b97c1b 100644 --- a/server/services/manager.go +++ b/server/services/manager.go @@ -72,13 +72,18 @@ func NewManager(c *cli.Context, store store.Store, setupForge SetupForge) (Manag return nil, err } + configService, err := setupConfigService(c, signaturePrivateKey) + if err != nil { + return nil, err + } + return &manager{ signaturePrivateKey: signaturePrivateKey, signaturePublicKey: signaturePublicKey, store: store, secret: setupSecretService(store), registry: setupRegistryService(store, c.String("docker-config")), - config: setupConfigService(c, signaturePrivateKey), + config: configService, environment: environment.Parse(c.StringSlice("environment")), forgeCache: ttlcache.New(ttlcache.WithDisableTouchOnHit[int64, forge.Forge]()), setupForge: setupForge, diff --git a/server/services/setup.go b/server/services/setup.go index c92ed9e3a..097a0215c 100644 --- a/server/services/setup.go +++ b/server/services/setup.go @@ -56,16 +56,20 @@ func setupSecretService(store store.Store) secret.Service { return secret.NewDB(store) } -func setupConfigService(c *cli.Context, privateSignatureKey crypto.PrivateKey) config.Service { +func setupConfigService(c *cli.Context, privateSignatureKey crypto.PrivateKey) (config.Service, error) { timeout := c.Duration("forge-timeout") - configFetcher := config.NewForge(timeout) + retries := c.Uint("forge-retry") + if retries == 0 { + return nil, fmt.Errorf("WOODPECKER_FORGE_RETRY can not be 0") + } + configFetcher := config.NewForge(timeout, retries) if endpoint := c.String("config-service-endpoint"); endpoint != "" { httpFetcher := config.NewHTTP(endpoint, privateSignatureKey) - return config.NewCombined(configFetcher, httpFetcher) + return config.NewCombined(configFetcher, httpFetcher), nil } - return configFetcher + return configFetcher, nil } // setupSignatureKeys generate or load key pair to sign webhooks requests (i.e. used for service extensions)