Path: blob/main/components/registry-facade/pkg/registry/http_client_test.go
2499 views
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package registry_test56import (7"context"8"errors"9"io"10"net/http"11"testing"1213"github.com/containerd/containerd/remotes"14"github.com/containerd/containerd/remotes/docker"15"github.com/google/go-cmp/cmp"16"github.com/hashicorp/go-retryablehttp"17"github.com/opencontainers/go-digest"1819"github.com/gitpod-io/gitpod/registry-facade/pkg/registry"20)2122func TestRetryableFetcher(t *testing.T) {23type Expectation struct {24Digest string25Error string26}2728tests := []struct {29Name string30Ref string31Retries bool32Expectation Expectation33}{34{35Name: "valid reference",36Ref: "docker.io/library/alpine@sha256:7580ece7963bfa863801466c0a488f11c86f85d9988051a9f9c68cb27f6b7872",37Retries: true,38Expectation: Expectation{Digest: "sha256:7580ece7963bfa863801466c0a488f11c86f85d9988051a9f9c68cb27f6b7872"},39},40{41Name: "invalid reference",42Ref: "docker.io/library/invalid-alpine",43Retries: false,44Expectation: Expectation{Error: "object required"},45},46}4748for _, test := range tests {49t.Run(test.Name, func(t *testing.T) {50var retries int51resolverFactory := func() remotes.Resolver {52client := registry.NewRetryableHTTPClient(53registry.WithHTTPClient(54&http.Client{55Transport: &failFirstErrorRoundTrip{rt: http.DefaultTransport},56},57),58registry.WithRequestLogHook(func(logger retryablehttp.Logger, req *http.Request, attempt int) {59retries = attempt60}),61)6263resolverOpts := docker.ResolverOptions{64Client: client,65}6667return docker.NewResolver(resolverOpts)68}6970ctx, cancel := context.WithCancel(context.Background())71defer cancel()7273resolver := resolverFactory()7475_, desc, err := resolver.Resolve(ctx, test.Ref)76if err != nil {77if !test.Retries && retries == 0 {78return79}8081t.Fatalf("cannot download ref: %+q", err)82}8384fetcher, err := resolver.Fetcher(context.Background(), test.Ref)85if err != nil {86t.Error(err)87}8889reader, err := fetcher.Fetch(ctx, desc)90if err != nil {91t.Error(err)92}9394var (95outcome Expectation96)9798data, err := io.ReadAll(reader)99if err != nil {100outcome.Error = err.Error()101}102103outcome.Digest = digest.FromBytes(data).String()104105if diff := cmp.Diff(test.Expectation, outcome); diff != "" {106t.Errorf("Info() mismatch (-want +got):\n%s", diff)107}108})109}110}111112type failFirstErrorRoundTrip struct {113rt http.RoundTripper114requests int115}116117func (rt *failFirstErrorRoundTrip) RoundTrip(req *http.Request) (resp *http.Response, err error) {118if rt.requests == 0 {119rt.requests += 1120return nil, errors.New("connection reset by peer")121}122123return rt.rt.RoundTrip(req)124}125126127