oras.land/oras-go/v2@v2.5.1-0.20240520045656-aef90e4d04c4/internal/registryutil/proxy_test.go (about)

     1  /*
     2  Copyright The ORAS Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package registryutil_test
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"io"
    22  	"net/http"
    23  	"net/http/httptest"
    24  	"net/url"
    25  	"reflect"
    26  	"strconv"
    27  	"strings"
    28  	"testing"
    29  
    30  	"github.com/opencontainers/go-digest"
    31  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    32  	"oras.land/oras-go/v2/internal/cas"
    33  	"oras.land/oras-go/v2/internal/registryutil"
    34  	"oras.land/oras-go/v2/registry/remote"
    35  )
    36  
    37  func TestProxy_FetchReference(t *testing.T) {
    38  	content := []byte(`{"manifests":[]}`)
    39  	desc := ocispec.Descriptor{
    40  		MediaType: ocispec.MediaTypeImageIndex,
    41  		Digest:    digest.FromBytes(content),
    42  		Size:      int64(len(content)),
    43  	}
    44  	ref := "foobar"
    45  	// prepare repository server
    46  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    47  		if r.Method != http.MethodHead && r.Method != http.MethodGet {
    48  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
    49  			w.WriteHeader(http.StatusMethodNotAllowed)
    50  			return
    51  		}
    52  		switch r.URL.Path {
    53  		case "/v2/test/manifests/" + desc.Digest.String(),
    54  			"/v2/test/manifests/" + ref:
    55  			if accept := r.Header.Get("Accept"); !strings.Contains(accept, desc.MediaType) {
    56  				t.Errorf("manifest not convertable: %s", accept)
    57  				w.WriteHeader(http.StatusBadRequest)
    58  				return
    59  			}
    60  			w.Header().Set("Content-Type", desc.MediaType)
    61  			w.Header().Set("Docker-Content-Digest", desc.Digest.String())
    62  			w.Header().Set("Content-Length", strconv.Itoa(int(desc.Size)))
    63  			if r.Method == http.MethodGet {
    64  				if _, err := w.Write(content); err != nil {
    65  					t.Errorf("failed to write %q: %v", r.URL, err)
    66  				}
    67  			}
    68  
    69  		default:
    70  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
    71  			w.WriteHeader(http.StatusNotFound)
    72  		}
    73  	}))
    74  	defer ts.Close()
    75  	uri, err := url.Parse(ts.URL)
    76  	if err != nil {
    77  		t.Fatalf("invalid test http server: %v", err)
    78  	}
    79  
    80  	repoName := uri.Host + "/test"
    81  	repo, err := remote.NewRepository(repoName)
    82  	if err != nil {
    83  		t.Fatalf("NewRepository() error = %v", err)
    84  	}
    85  	repo.PlainHTTP = true
    86  
    87  	s := registryutil.NewProxy(repo, cas.NewMemory())
    88  	ctx := context.Background()
    89  
    90  	// first FetchReference
    91  	exists, err := s.Exists(ctx, desc)
    92  	if err != nil {
    93  		t.Fatal("Proxy.Exists() error =", err)
    94  	}
    95  	if !exists {
    96  		t.Errorf("Proxy.Exists() = %v, want %v", exists, true)
    97  	}
    98  	gotDesc, rc, err := s.FetchReference(ctx, ref)
    99  	if err != nil {
   100  		t.Fatal("Proxy.FetchReference() error =", err)
   101  	}
   102  	if !reflect.DeepEqual(gotDesc, desc) {
   103  		t.Errorf("Proxy.FetchReference() = %v, want %v", gotDesc, desc)
   104  	}
   105  	got, err := io.ReadAll(rc)
   106  	if err != nil {
   107  		t.Fatal("Proxy.FetchReference().Read() error =", err)
   108  	}
   109  	err = rc.Close()
   110  	if err != nil {
   111  		t.Error("Proxy.FetchReference().Close() error =", err)
   112  	}
   113  	if !bytes.Equal(got, content) {
   114  		t.Errorf("Proxy.FetchReference() = %v, want %v", got, content)
   115  	}
   116  
   117  	// the subsequent fetch should not touch base CAS
   118  	// nil base will generate panic if the base CAS is touched
   119  	s.ReadOnlyStorage = nil
   120  
   121  	exists, err = s.Exists(ctx, desc)
   122  	if err != nil {
   123  		t.Fatal("Proxy.Exists() error =", err)
   124  	}
   125  	if !exists {
   126  		t.Errorf("Proxy.Exists() = %v, want %v", exists, true)
   127  	}
   128  	rc, err = s.Fetch(ctx, desc)
   129  	if err != nil {
   130  		t.Fatal("Proxy.Fetch() error =", err)
   131  	}
   132  	got, err = io.ReadAll(rc)
   133  	if err != nil {
   134  		t.Fatal("Proxy.Fetch().Read() error =", err)
   135  	}
   136  	err = rc.Close()
   137  	if err != nil {
   138  		t.Error("Proxy.Fetch().Close() error =", err)
   139  	}
   140  	if !bytes.Equal(got, content) {
   141  		t.Errorf("Proxy.Fetch() = %v, want %v", got, content)
   142  	}
   143  
   144  	// repeated FetchReference should succeed
   145  	exists, err = s.Exists(ctx, desc)
   146  	if err != nil {
   147  		t.Fatal("Proxy.Exists() error =", err)
   148  	}
   149  	if !exists {
   150  		t.Errorf("Proxy.Exists() = %v, want %v", exists, true)
   151  	}
   152  	gotDesc, rc, err = s.FetchReference(ctx, ref)
   153  	if err != nil {
   154  		t.Fatal("Proxy.FetchReference() error =", err)
   155  	}
   156  	if !reflect.DeepEqual(gotDesc, desc) {
   157  		t.Errorf("Proxy.FetchReference() = %v, want %v", gotDesc, desc)
   158  	}
   159  	got, err = io.ReadAll(rc)
   160  	if err != nil {
   161  		t.Fatal("Proxy.Fetch().Read() error =", err)
   162  	}
   163  	err = rc.Close()
   164  	if err != nil {
   165  		t.Error("Proxy.Fetch().Close() error =", err)
   166  	}
   167  	if !bytes.Equal(got, content) {
   168  		t.Errorf("Proxy.Fetch() = %v, want %v", got, content)
   169  	}
   170  }