Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add configuration for custom bundle resolver backoff #8574

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions docs/bundle-resolver.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,14 @@ for the name, namespace and defaults that the resolver ships with.

### Options

| Option Name | Description | Example Values |
|---------------------------|--------------------------------------------------------------|-----------------------|
| `default-kind` | The default layer kind in the bundle image. | `task`, `pipeline` |
| Option Name | Description | Example Values |
|----------------------|-------------------------------------------------------------------|-----------------------|
| `backoff-duration` | The initial duration for a backoff. | `500ms`, `2s` |
| `backoff-factor` | The factor by which the sleep duration increases every step. | `2.5`, `4.0` |
| `backoff-jitter` | A random amount of additioan sleep between 0 andduration * jitter.| `0.1`, `0.5` |
| `backoff-steps` | The number of backoffs to attempt. | `3`, `7` |
| `backoff-cap` | The maxumum backoff duration. If reached, remaining steps are zeroed.| `10s`, `20s` |
| `default-kind` | The default layer kind in the bundle image. | `task`, `pipeline` |

## Usage

Expand Down
12 changes: 10 additions & 2 deletions pkg/resolution/resolver/bundle/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,17 @@ func retrieveImage(ctx context.Context, keychain authn.Keychain, ref string) (st
if err != nil {
return "", nil, fmt.Errorf("%s is an unparseable image reference: %w", ref, err)
}
customRetryBackoff, err := GetBundleResolverBackoff(ctx)
if err == nil {
img, err := remote.Image(imgRef, remote.WithAuthFromKeychain(keychain), remote.WithContext(ctx),
remote.WithRetryBackoff(customRetryBackoff))

img, err := remote.Image(imgRef, remote.WithAuthFromKeychain(keychain), remote.WithContext(ctx))
return imgRef.Context().Name(), img, err
return imgRef.Context().Name(), img, err
} else {
img, err := remote.Image(imgRef, remote.WithAuthFromKeychain(keychain), remote.WithContext(ctx))

return imgRef.Context().Name(), img, err
}
}

// checkImageCompliance will perform common checks to ensure the Tekton Bundle is compliant to our spec.
Expand Down
91 changes: 90 additions & 1 deletion pkg/resolution/resolver/bundle/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,103 @@ limitations under the License.

package bundle

import (
"context"
"fmt"
"strconv"
"time"

"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/tektoncd/pipeline/pkg/resolution/resolver/framework"
)

const (
// ConfigServiceAccount is the configuration field name for controlling
// the Service Account name to use for bundle requests.
ConfigServiceAccount = "default-service-account"
// ConfigKind is the configuration field name for controlling
// what the layer name in the bundle image is.
ConfigKind = "default-kind"
// DefaultTimeoutKey is the configuration field name for controlling
// ConfigTimeoutKey is the configuration field name for controlling
// the maximum duration of a resolution request for a file from registry.
ConfigTimeoutKey = "fetch-timeout"
// ConfigBackoffDuration is the configuration field name for controlling
// the initial duration of a backoff when a bundle resolution fails
ConfigBackoffDuration = "backoff-duration"
DefaultBackoffDuration = 2.0 * time.Second
// ConfigBackoffFactor is the configuration field name for controlling
// the factor by which successive backoffs will increase when a bundle
// resolution fails
ConfigBackoffFactor = "backoff-factor"
DefaultBackoffFactor = 2.0
// ConfigBackoffJitter is the configuration field name for controlling
// the randomness applied to backoff durations when a bundle resolution fails
ConfigBackoffJitter = "backoff-jitter"
DefaultBackoffJitter = 0.1
// ConfigBackoffSteps is the configuration field name for controlling
// the number of attempted backoffs to retry when a bundle resolution fails
ConfigBackoffSteps = "backoff-steps"
DefaultBackoffSteps = 2
// ConfigBackoffCap is the configuration field name for controlling
// the maximum duration to try when backing off
ConfigBackoffCap = "backoff-cap"
DefaultBackoffCap = 10 * time.Second
)

// GetBundleResolverBackoff returns a remote.Backoff to
// be passed when resolving remote images. This can be configured with the
// backoff-duration, backoff-factor, backoff-jitter, backoff-steps, and backoff-cap
// fields in the bundle-resolver-config ConfigMap.
func GetBundleResolverBackoff(ctx context.Context) (remote.Backoff, error) {
conf := framework.GetResolverConfigFromContext(ctx)

customRetryBackoff := remote.Backoff{
Duration: DefaultBackoffDuration,
Factor: DefaultBackoffFactor,
Jitter: DefaultBackoffJitter,
Steps: DefaultBackoffSteps,
Cap: DefaultBackoffCap,
}
if v, ok := conf[ConfigBackoffDuration]; ok {
var err error
duration, err := time.ParseDuration(v)
if err != nil {
return customRetryBackoff, fmt.Errorf("error parsing backoff duration value %s: %w", v, err)
}
customRetryBackoff.Duration = duration
}
if v, ok := conf[ConfigBackoffFactor]; ok {
var err error
factor, err := strconv.ParseFloat(v, 64)
if err != nil {
return customRetryBackoff, fmt.Errorf("error parsing backoff factor value %s: %w", v, err)
}
customRetryBackoff.Factor = factor
}
if v, ok := conf[ConfigBackoffJitter]; ok {
var err error
jitter, err := strconv.ParseFloat(v, 64)
if err != nil {
return customRetryBackoff, fmt.Errorf("error parsing backoff jitter value %s: %w", v, err)
}
customRetryBackoff.Jitter = jitter
}
if v, ok := conf[ConfigBackoffSteps]; ok {
var err error
steps, err := strconv.Atoi(v)
if err != nil {
return customRetryBackoff, fmt.Errorf("error parsing backoff steps value %s: %w", v, err)
}
customRetryBackoff.Steps = steps
}
if v, ok := conf[ConfigBackoffCap]; ok {
var err error
cap, err := time.ParseDuration(v)
if err != nil {
return customRetryBackoff, fmt.Errorf("error parsing backoff steps value %s: %w", v, err)
}
customRetryBackoff.Cap = cap
}

return customRetryBackoff, nil
}
39 changes: 39 additions & 0 deletions pkg/resolution/resolver/bundle/resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"fmt"
"net/http/httptest"
"net/url"
"strconv"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -740,3 +741,41 @@ func TestGetResolutionTimeoutCustom(t *testing.T) {
t.Fatalf("expected timeout from config to be returned")
}
}

func TestGetResolutionBackoffCustom(t *testing.T) {
// resolver := bundle.Resolver{}
// defaultTimeout := 30 * time.Minute
configBackoffDuration := 7.0 * time.Second
configBackoffFactor := 7.0
configBackoffJitter := 0.5
configBackoffSteps := 3
configBackoffCap := 20 * time.Second
config := map[string]string{
bundle.ConfigBackoffDuration: configBackoffDuration.String(),
bundle.ConfigBackoffFactor: strconv.FormatFloat(configBackoffFactor, 'f', -1, 64),
bundle.ConfigBackoffJitter: strconv.FormatFloat(configBackoffJitter, 'f', -1, 64),
bundle.ConfigBackoffSteps: strconv.Itoa(configBackoffSteps),
bundle.ConfigBackoffCap: configBackoffCap.String(),
}
ctx := framework.InjectResolverConfigToContext(context.Background(), config)
backoffConfig, err := bundle.GetBundleResolverBackoff(ctx)
// timeout, err := resolver.GetResolutionTimeout(ctx, defaultTimeout, map[string]string{})
if err != nil {
t.Fatalf("couldn't get backoff config: %v", err)
}
if backoffConfig.Duration != configBackoffDuration {
t.Fatalf("expected duration from config to be returned")
}
if backoffConfig.Factor != configBackoffFactor {
t.Fatalf("expected backoff from config to be returned")
}
if backoffConfig.Jitter != configBackoffJitter {
t.Fatalf("expected jitter from config to be returned")
}
if backoffConfig.Steps != configBackoffSteps {
t.Fatalf("expected steps from config to be returned")
}
if backoffConfig.Cap != configBackoffCap {
t.Fatalf("expected steps from config to be returned")
}
}