2015-07-29 18:12:01 +00:00
package proxy
import (
2017-08-11 22:31:16 +00:00
"context"
2015-07-29 18:12:01 +00:00
"io"
2016-02-11 02:07:28 +00:00
"sync"
2015-07-29 18:12:01 +00:00
"testing"
2020-08-24 11:18:39 +00:00
"github.com/distribution/distribution/v3"
"github.com/distribution/distribution/v3/manifest"
2023-05-09 11:18:47 +00:00
"github.com/distribution/distribution/v3/manifest/schema1" //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
2020-08-24 11:18:39 +00:00
"github.com/distribution/distribution/v3/reference"
"github.com/distribution/distribution/v3/registry/client/auth"
"github.com/distribution/distribution/v3/registry/client/auth/challenge"
"github.com/distribution/distribution/v3/registry/proxy/scheduler"
"github.com/distribution/distribution/v3/registry/storage"
"github.com/distribution/distribution/v3/registry/storage/cache/memory"
"github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
"github.com/distribution/distribution/v3/testutil"
2015-07-29 18:12:01 +00:00
"github.com/docker/libtrust"
2016-12-17 00:28:34 +00:00
"github.com/opencontainers/go-digest"
2015-07-29 18:12:01 +00:00
)
type statsManifest struct {
manifests distribution . ManifestService
stats map [ string ] int
}
type manifestStoreTestEnv struct {
manifestDigest digest . Digest // digest of the signed manifest in the local storage
manifests proxyManifestStore
}
func ( te manifestStoreTestEnv ) LocalStats ( ) * map [ string ] int {
ls := te . manifests . localManifests . ( statsManifest ) . stats
return & ls
}
func ( te manifestStoreTestEnv ) RemoteStats ( ) * map [ string ] int {
rs := te . manifests . remoteManifests . ( statsManifest ) . stats
return & rs
}
2015-08-21 04:50:15 +00:00
func ( sm statsManifest ) Delete ( ctx context . Context , dgst digest . Digest ) error {
2015-07-29 18:12:01 +00:00
sm . stats [ "delete" ] ++
2015-08-21 04:50:15 +00:00
return sm . manifests . Delete ( ctx , dgst )
2015-07-29 18:12:01 +00:00
}
2015-08-21 04:50:15 +00:00
func ( sm statsManifest ) Exists ( ctx context . Context , dgst digest . Digest ) ( bool , error ) {
2015-07-29 18:12:01 +00:00
sm . stats [ "exists" ] ++
2015-08-21 04:50:15 +00:00
return sm . manifests . Exists ( ctx , dgst )
2015-07-29 18:12:01 +00:00
}
2015-08-21 04:50:15 +00:00
func ( sm statsManifest ) Get ( ctx context . Context , dgst digest . Digest , options ... distribution . ManifestServiceOption ) ( distribution . Manifest , error ) {
2015-07-29 18:12:01 +00:00
sm . stats [ "get" ] ++
2015-08-21 04:50:15 +00:00
return sm . manifests . Get ( ctx , dgst )
2015-07-29 18:12:01 +00:00
}
2015-08-21 04:50:15 +00:00
func ( sm statsManifest ) Put ( ctx context . Context , manifest distribution . Manifest , options ... distribution . ManifestServiceOption ) ( digest . Digest , error ) {
2015-07-29 18:12:01 +00:00
sm . stats [ "put" ] ++
2015-08-21 04:50:15 +00:00
return sm . manifests . Put ( ctx , manifest )
2015-07-29 18:12:01 +00:00
}
2016-02-11 02:07:28 +00:00
type mockChallenger struct {
sync . Mutex
count int
}
// Called for remote operations only
2016-02-17 18:42:34 +00:00
func ( m * mockChallenger ) tryEstablishChallenges ( context . Context ) error {
m . Lock ( )
defer m . Unlock ( )
m . count ++
return nil
}
func ( m * mockChallenger ) credentialStore ( ) auth . CredentialStore {
return nil
}
2016-11-08 01:13:56 +00:00
func ( m * mockChallenger ) challengeManager ( ) challenge . Manager {
2016-02-11 02:07:28 +00:00
return nil
}
2015-07-29 18:12:01 +00:00
func newManifestStoreTestEnv ( t * testing . T , name , tag string ) * manifestStoreTestEnv {
2017-01-14 01:06:03 +00:00
nameRef , err := reference . WithName ( name )
2015-12-15 22:35:23 +00:00
if err != nil {
t . Fatalf ( "unable to parse reference: %s" , err )
}
2016-04-07 00:01:30 +00:00
k , err := libtrust . GenerateECP256PrivateKey ( )
if err != nil {
t . Fatal ( err )
}
2015-12-15 22:35:23 +00:00
2015-07-29 18:12:01 +00:00
ctx := context . Background ( )
2016-04-07 00:01:30 +00:00
truthRegistry , err := storage . NewRegistry ( ctx , inmemory . New ( ) ,
2022-07-13 00:42:48 +00:00
storage . BlobDescriptorCacheProvider ( memory . NewInMemoryBlobDescriptorCacheProvider ( memory . UnlimitedSize ) ) ,
2017-12-18 23:06:04 +00:00
storage . Schema1SigningKey ( k ) ,
storage . EnableSchema1 )
2015-08-18 17:56:27 +00:00
if err != nil {
t . Fatalf ( "error creating registry: %v" , err )
}
2015-12-15 22:35:23 +00:00
truthRepo , err := truthRegistry . Repository ( ctx , nameRef )
2015-07-29 18:12:01 +00:00
if err != nil {
t . Fatalf ( "unexpected error getting repo: %v" , err )
}
tr , err := truthRepo . Manifests ( ctx )
if err != nil {
t . Fatal ( err . Error ( ) )
}
truthManifests := statsManifest {
manifests : tr ,
stats : make ( map [ string ] int ) ,
}
2016-10-15 00:03:08 +00:00
manifestDigest , err := populateRepo ( ctx , t , truthRepo , name , tag )
2015-07-29 18:12:01 +00:00
if err != nil {
t . Fatalf ( err . Error ( ) )
}
2022-07-13 00:42:48 +00:00
localRegistry , err := storage . NewRegistry ( ctx , inmemory . New ( ) , storage . BlobDescriptorCacheProvider ( memory . NewInMemoryBlobDescriptorCacheProvider ( memory . UnlimitedSize ) ) , storage . EnableRedirect , storage . DisableDigestResumption , storage . Schema1SigningKey ( k ) , storage . EnableSchema1 )
2015-08-18 17:56:27 +00:00
if err != nil {
t . Fatalf ( "error creating registry: %v" , err )
}
2015-12-15 22:35:23 +00:00
localRepo , err := localRegistry . Repository ( ctx , nameRef )
2015-07-29 18:12:01 +00:00
if err != nil {
t . Fatalf ( "unexpected error getting repo: %v" , err )
}
lr , err := localRepo . Manifests ( ctx )
if err != nil {
t . Fatal ( err . Error ( ) )
}
localManifests := statsManifest {
manifests : lr ,
stats : make ( map [ string ] int ) ,
}
s := scheduler . New ( ctx , inmemory . New ( ) , "/scheduler-state.json" )
return & manifestStoreTestEnv {
manifestDigest : manifestDigest ,
manifests : proxyManifestStore {
ctx : ctx ,
localManifests : localManifests ,
remoteManifests : truthManifests ,
scheduler : s ,
2016-01-27 00:42:10 +00:00
repositoryName : nameRef ,
2016-02-11 02:07:28 +00:00
authChallenger : & mockChallenger { } ,
2015-07-29 18:12:01 +00:00
} ,
}
}
2016-10-15 00:03:08 +00:00
func populateRepo ( ctx context . Context , t * testing . T , repository distribution . Repository , name , tag string ) ( digest . Digest , error ) {
2023-05-09 11:18:47 +00:00
m := schema1 . Manifest { //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
2015-07-29 18:12:01 +00:00
Versioned : manifest . Versioned {
SchemaVersion : 1 ,
} ,
Name : name ,
Tag : tag ,
}
for i := 0 ; i < 2 ; i ++ {
wr , err := repository . Blobs ( ctx ) . Create ( ctx )
if err != nil {
t . Fatalf ( "unexpected error creating test upload: %v" , err )
}
2019-02-05 00:01:04 +00:00
rs , dgst , err := testutil . CreateRandomTarFile ( )
2015-07-29 18:12:01 +00:00
if err != nil {
t . Fatalf ( "unexpected error generating test layer file" )
}
if _ , err := io . Copy ( wr , rs ) ; err != nil {
t . Fatalf ( "unexpected error copying to upload: %v" , err )
}
if _ , err := wr . Commit ( ctx , distribution . Descriptor { Digest : dgst } ) ; err != nil {
t . Fatalf ( "unexpected error finishing upload: %v" , err )
}
}
pk , err := libtrust . GenerateECP256PrivateKey ( )
if err != nil {
t . Fatalf ( "unexpected error generating private key: %v" , err )
}
2023-05-09 11:18:47 +00:00
sm , err := schema1 . Sign ( & m , pk ) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
2015-07-29 18:12:01 +00:00
if err != nil {
t . Fatalf ( "error signing manifest: %v" , err )
}
ms , err := repository . Manifests ( ctx )
if err != nil {
t . Fatalf ( err . Error ( ) )
}
2015-08-21 04:50:15 +00:00
dgst , err := ms . Put ( ctx , sm )
2015-07-29 18:12:01 +00:00
if err != nil {
t . Fatalf ( "unexpected errors putting manifest: %v" , err )
}
2015-08-21 04:50:15 +00:00
return dgst , nil
2015-07-29 18:12:01 +00:00
}
// TestProxyManifests contains basic acceptance tests
// for the pull-through behavior
func TestProxyManifests ( t * testing . T ) {
name := "foo/bar"
env := newManifestStoreTestEnv ( t , name , "latest" )
localStats := env . LocalStats ( )
remoteStats := env . RemoteStats ( )
2015-08-21 04:50:15 +00:00
ctx := context . Background ( )
2015-07-29 18:12:01 +00:00
// Stat - must check local and remote
2015-08-21 04:50:15 +00:00
exists , err := env . manifests . Exists ( ctx , env . manifestDigest )
2015-07-29 18:12:01 +00:00
if err != nil {
2016-02-23 21:33:38 +00:00
t . Fatalf ( "Error checking existence" )
2015-07-29 18:12:01 +00:00
}
if ! exists {
2018-09-13 06:08:39 +00:00
t . Errorf ( "Unexpected non-existent manifest" )
2015-07-29 18:12:01 +00:00
}
2015-08-21 04:50:15 +00:00
if ( * localStats ) [ "exists" ] != 1 && ( * remoteStats ) [ "exists" ] != 1 {
t . Errorf ( "Unexpected exists count : \n%v \n%v" , localStats , remoteStats )
2015-07-29 18:12:01 +00:00
}
2016-02-11 02:07:28 +00:00
if env . manifests . authChallenger . ( * mockChallenger ) . count != 1 {
t . Fatalf ( "Expected 1 auth challenge, got %#v" , env . manifests . authChallenger )
}
2015-07-29 18:12:01 +00:00
// Get - should succeed and pull manifest into local
2015-08-21 04:50:15 +00:00
_ , err = env . manifests . Get ( ctx , env . manifestDigest )
2015-07-29 18:12:01 +00:00
if err != nil {
t . Fatal ( err )
}
2015-08-21 04:50:15 +00:00
2015-07-29 18:12:01 +00:00
if ( * localStats ) [ "get" ] != 1 && ( * remoteStats ) [ "get" ] != 1 {
t . Errorf ( "Unexpected get count" )
}
if ( * localStats ) [ "put" ] != 1 {
t . Errorf ( "Expected local put" )
}
2016-02-11 02:07:28 +00:00
if env . manifests . authChallenger . ( * mockChallenger ) . count != 2 {
t . Fatalf ( "Expected 2 auth challenges, got %#v" , env . manifests . authChallenger )
}
2015-07-29 18:12:01 +00:00
// Stat - should only go to local
2015-08-21 04:50:15 +00:00
exists , err = env . manifests . Exists ( ctx , env . manifestDigest )
2015-07-29 18:12:01 +00:00
if err != nil {
t . Fatal ( err )
}
if ! exists {
2018-09-13 06:08:39 +00:00
t . Errorf ( "Unexpected non-existent manifest" )
2015-07-29 18:12:01 +00:00
}
2015-08-21 04:50:15 +00:00
if ( * localStats ) [ "exists" ] != 2 && ( * remoteStats ) [ "exists" ] != 1 {
2015-07-29 18:12:01 +00:00
t . Errorf ( "Unexpected exists count" )
}
2016-02-11 02:07:28 +00:00
if env . manifests . authChallenger . ( * mockChallenger ) . count != 2 {
t . Fatalf ( "Expected 2 auth challenges, got %#v" , env . manifests . authChallenger )
}
// Get proxied - won't require another authchallenge
2015-08-21 04:50:15 +00:00
_ , err = env . manifests . Get ( ctx , env . manifestDigest )
2015-07-29 18:12:01 +00:00
if err != nil {
t . Fatal ( err )
}
2016-02-11 02:07:28 +00:00
if env . manifests . authChallenger . ( * mockChallenger ) . count != 2 {
t . Fatalf ( "Expected 2 auth challenges, got %#v" , env . manifests . authChallenger )
2015-07-29 18:12:01 +00:00
}
}
2020-08-28 00:07:35 +00:00
func TestProxyManifestsWithoutScheduler ( t * testing . T ) {
name := "foo/bar"
env := newManifestStoreTestEnv ( t , name , "latest" )
env . manifests . scheduler = nil
ctx := context . Background ( )
exists , err := env . manifests . Exists ( ctx , env . manifestDigest )
if err != nil {
t . Fatalf ( "Error checking existence" )
}
if ! exists {
t . Errorf ( "Unexpected non-existent manifest" )
}
// Get - should succeed without scheduler
_ , err = env . manifests . Get ( ctx , env . manifestDigest )
if err != nil {
t . Fatal ( err )
}
}