// Package warehouse implements a testing framework extension for working with // Warehouse OCI packages and registries. package warehouse import ( "flag" "fmt" "net/http/httptest" "testing" "github.com/google/go-containerregistry/pkg/name" "edge-infra.dev/pkg/f8n/warehouse/oci" "edge-infra.dev/pkg/f8n/warehouse/oci/remote" "edge-infra.dev/pkg/f8n/warehouse/whtest/registry" "edge-infra.dev/test/f2" "edge-infra.dev/test/f2/fctx" "edge-infra.dev/test/f2/integration" ) // TODO: integrate real registry for integration tests // TODO: clean up artifacts pushed to real registries, provide option for skipping clean up // Registry is a f2 extension that resolves to a in-memory registry for unit // tests and a fully qualified URL to a remote registry for integration tests. type Registry struct { // URL is the computed test repository URL used when publishing OCI packages // from tests. The package name will be appended to create the full URL. URL string // options forwards on options to the gccr registry options []registry.Option // in-memory registry instantiated for non-integration test runs registry *httptest.Server } // New is a function that can forward options func New(opts ...registry.Option) *Registry { return &Registry{ options: opts, } } // FromContext attempts to fetch an instance of Registry from the test context and // returns an error if it is not discovered. func FromContext(ctx fctx.Context) (*Registry, error) { v := fctx.ValueFrom[Registry](ctx) if v == nil { return nil, fmt.Errorf("%w: warehouse.Registry extension", fctx.ErrNotFound) } return v, nil } // FromContextT is a testing variant of FromContext that immediately fails the // test if Registry isnt presnt in the testing context. func FromContextT(ctx fctx.Context, t *testing.T) *Registry { return fctx.ValueFromT[Registry](ctx, t) } // Push publishes the input OCI artifact to the resolved registry URL func (r *Registry) Push(a oci.Artifact, pkgName, tag string, opts ...remote.Option) error { // if this is a unit test, explicitly remove auth keychains when we interact // with local test registry server if integration.IsL1() { opts = append(opts, remote.WithoutAuth()) } refstr := fmt.Sprintf("%s/%s:%s", r.URL, pkgName, tag) dst, err := name.ParseReference(refstr) if err != nil { return fmt.Errorf("failed to parse destination %s: %w", refstr, err) } return remote.Write(a, dst, opts...) } func (r *Registry) RegisterFns(f f2.Framework) { switch { case integration.IsL1(): // Otherwise register funcs for creating in-memory registry and tearing it // down at the end of the test run f.Setup(func(ctx fctx.Context) (fctx.Context, error) { u, registry, err := registry.New(r.options...) if err != nil { return ctx, err } r.registry = registry r.URL = repoURL(u.Host, ctx.RunID) return ctx, nil }) f.Teardown(func(ctx fctx.Context) (fctx.Context, error) { r.registry.Close() return ctx, nil }) case integration.IsL2(): // If integration test, register a setup function that: // - check for required configuration, // - create final repo URL f.Setup(func(ctx fctx.Context) (fctx.Context, error) { if r.URL == "" { return ctx, fmt.Errorf( "%w: integration test requires --warehouse-repo", f2.ErrSkip, ) } r.URL = repoURL(r.URL, ctx.RunID) return ctx, nil }) } } // BindFlags registers test flags for the framework extension. func (r *Registry) BindFlags(fs *flag.FlagSet) { fs.StringVar(&r.URL, "warehouse-repo", "", "the OCI repo URL to use for remote operations. the test run ID is appended "+ "to the value to improve test isolation", ) } // IntoContext stores the framework extension in the test context. func (r *Registry) IntoContext(ctx fctx.Context) fctx.Context { return fctx.ValueInto(ctx, r) } // repoURL provides a unique repository root for test suites to push pallets // without colliding with other concurrent test runs func repoURL(base, context string) string { return fmt.Sprintf("%s/%s", base, context) }