...

Source file src/edge-infra.dev/test/f2/x/warehouse/registry.go

Documentation: edge-infra.dev/test/f2/x/warehouse

     1  // Package warehouse implements a testing framework extension for working with
     2  // Warehouse OCI packages and registries.
     3  package warehouse
     4  
     5  import (
     6  	"flag"
     7  	"fmt"
     8  	"net/http/httptest"
     9  	"testing"
    10  
    11  	"github.com/google/go-containerregistry/pkg/name"
    12  
    13  	"edge-infra.dev/pkg/f8n/warehouse/oci"
    14  	"edge-infra.dev/pkg/f8n/warehouse/oci/remote"
    15  	"edge-infra.dev/pkg/f8n/warehouse/whtest/registry"
    16  	"edge-infra.dev/test/f2"
    17  	"edge-infra.dev/test/f2/fctx"
    18  	"edge-infra.dev/test/f2/integration"
    19  )
    20  
    21  // TODO: integrate real registry for integration tests
    22  // TODO: clean up artifacts pushed to real registries, provide option for skipping clean up
    23  
    24  // Registry is a f2 extension that resolves to a in-memory registry for unit
    25  // tests and a fully qualified URL to a remote registry for integration tests.
    26  type Registry struct {
    27  	// URL is the computed test repository URL used when publishing OCI packages
    28  	// from tests. The package name will be appended to create the full URL.
    29  	URL string
    30  
    31  	// options forwards on options to the gccr registry
    32  	options []registry.Option
    33  
    34  	// in-memory registry instantiated for non-integration test runs
    35  	registry *httptest.Server
    36  }
    37  
    38  // New is a function that can forward options
    39  func New(opts ...registry.Option) *Registry {
    40  	return &Registry{
    41  		options: opts,
    42  	}
    43  }
    44  
    45  // FromContext attempts to fetch an instance of Registry from the test context and
    46  // returns an error if it is not discovered.
    47  func FromContext(ctx fctx.Context) (*Registry, error) {
    48  	v := fctx.ValueFrom[Registry](ctx)
    49  	if v == nil {
    50  		return nil, fmt.Errorf("%w: warehouse.Registry extension", fctx.ErrNotFound)
    51  	}
    52  	return v, nil
    53  }
    54  
    55  // FromContextT is a testing variant of FromContext that immediately fails the
    56  // test if Registry isnt presnt in the testing context.
    57  func FromContextT(ctx fctx.Context, t *testing.T) *Registry {
    58  	return fctx.ValueFromT[Registry](ctx, t)
    59  }
    60  
    61  // Push publishes the input OCI artifact to the resolved registry URL
    62  func (r *Registry) Push(a oci.Artifact, pkgName, tag string, opts ...remote.Option) error {
    63  	// if this is a unit test, explicitly remove auth keychains when we interact
    64  	// with local test registry server
    65  	if integration.IsL1() {
    66  		opts = append(opts, remote.WithoutAuth())
    67  	}
    68  
    69  	refstr := fmt.Sprintf("%s/%s:%s", r.URL, pkgName, tag)
    70  	dst, err := name.ParseReference(refstr)
    71  	if err != nil {
    72  		return fmt.Errorf("failed to parse destination %s: %w", refstr, err)
    73  	}
    74  
    75  	return remote.Write(a, dst, opts...)
    76  }
    77  
    78  func (r *Registry) RegisterFns(f f2.Framework) {
    79  	switch {
    80  	case integration.IsL1():
    81  		// Otherwise register funcs for creating in-memory registry and tearing it
    82  		// down at the end of the test run
    83  		f.Setup(func(ctx fctx.Context) (fctx.Context, error) {
    84  			u, registry, err := registry.New(r.options...)
    85  			if err != nil {
    86  				return ctx, err
    87  			}
    88  
    89  			r.registry = registry
    90  			r.URL = repoURL(u.Host, ctx.RunID)
    91  
    92  			return ctx, nil
    93  		})
    94  		f.Teardown(func(ctx fctx.Context) (fctx.Context, error) {
    95  			r.registry.Close()
    96  			return ctx, nil
    97  		})
    98  	case integration.IsL2():
    99  		// If integration test, register a setup function that:
   100  		// - check for required configuration,
   101  		// - create final repo URL
   102  		f.Setup(func(ctx fctx.Context) (fctx.Context, error) {
   103  			if r.URL == "" {
   104  				return ctx, fmt.Errorf(
   105  					"%w: integration test requires --warehouse-repo", f2.ErrSkip,
   106  				)
   107  			}
   108  
   109  			r.URL = repoURL(r.URL, ctx.RunID)
   110  
   111  			return ctx, nil
   112  		})
   113  	}
   114  }
   115  
   116  // BindFlags registers test flags for the framework extension.
   117  func (r *Registry) BindFlags(fs *flag.FlagSet) {
   118  	fs.StringVar(&r.URL,
   119  		"warehouse-repo",
   120  		"",
   121  		"the OCI repo URL to use for remote operations. the test run ID is appended "+
   122  			"to the value to improve test isolation",
   123  	)
   124  }
   125  
   126  // IntoContext stores the framework extension in the test context.
   127  func (r *Registry) IntoContext(ctx fctx.Context) fctx.Context {
   128  	return fctx.ValueInto(ctx, r)
   129  }
   130  
   131  // repoURL provides a unique repository root for test suites to push pallets
   132  // without colliding with other concurrent test runs
   133  func repoURL(base, context string) string {
   134  	return fmt.Sprintf("%s/%s", base, context)
   135  }
   136  

View as plain text