mirror of https://github.com/knative/func.git
165 lines
4.4 KiB
Go
165 lines
4.4 KiB
Go
package oci
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
fn "knative.dev/func/pkg/functions"
|
|
"knative.dev/func/pkg/oci/mock"
|
|
. "knative.dev/func/pkg/testing"
|
|
|
|
"github.com/google/go-containerregistry/pkg/registry"
|
|
)
|
|
|
|
// TestPusher_Push ensures the base case that the pusher contacts the
|
|
// registry with a correctly formed request.
|
|
func TestPusher_Push(t *testing.T) {
|
|
var (
|
|
root, done = Mktemp(t)
|
|
verbose = false
|
|
insecure = true
|
|
anon = true
|
|
success = false
|
|
err error
|
|
)
|
|
defer done()
|
|
|
|
// Start a handler on an OS-chosen port which confirms that an incoming
|
|
// requests looks for the most part like what we'd expect to see from a
|
|
// container push.
|
|
regLog := io.Discard
|
|
if verbose {
|
|
regLog = os.Stderr
|
|
}
|
|
regHandler := registry.New(registry.Logger(log.New(regLog, "img reg handler: ", log.LstdFlags)))
|
|
handler := http.NewServeMux()
|
|
handler.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
|
|
regHandler.ServeHTTP(res, req)
|
|
if req.Method == http.MethodPut && req.URL.Path == "/v2/funcs/f/manifests/latest" {
|
|
success = true
|
|
}
|
|
})
|
|
l, err := net.Listen("tcp4", "127.0.0.1:")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
s := http.Server{Handler: handler}
|
|
go func() {
|
|
if err = s.Serve(l); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
fmt.Fprintf(os.Stderr, "error serving: %v", err)
|
|
}
|
|
}()
|
|
defer s.Close()
|
|
|
|
// Create and push a function
|
|
client := fn.New(
|
|
fn.WithBuilder(NewBuilder("", false)),
|
|
fn.WithPusher(NewPusher(insecure, anon, verbose)))
|
|
|
|
f := fn.Function{Root: root, Runtime: "go", Name: "f", Registry: l.Addr().String() + "/funcs"}
|
|
|
|
if f, err = client.Init(f); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if f, err = client.Build(context.Background(), f); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if _, _, err = client.Push(context.Background(), f); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Confirm the handler received at least on PUT request containing
|
|
// an image index.
|
|
if !success {
|
|
t.Fatal("did not receive the image index JSON")
|
|
}
|
|
}
|
|
|
|
// TestPusher_BasicAuth ensures that the pusher authenticates via basic auth when
|
|
// supplied with a username/password via the context.
|
|
func TestPusher_BasicAuth(t *testing.T) {
|
|
var (
|
|
root, done = Mktemp(t)
|
|
username = "username"
|
|
password = "password"
|
|
verbose = false
|
|
successCh = make(chan bool, 100) // Many successes are queued on push
|
|
err error
|
|
)
|
|
defer done()
|
|
|
|
// A mock registry with middleware which performs basic auth
|
|
server := mock.NewRegistry()
|
|
server.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
|
|
u, p, ok := r.BasicAuth()
|
|
if !ok {
|
|
// no header. ask for auth
|
|
w.Header().Add("www-authenticate", "Basic realm=\"Registry Realm\"")
|
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
} else if u != username || p != password {
|
|
// header exists, but creds are either missing or incorrect
|
|
t.Fatalf("Unauthorized. Expected user %q pass %q, got user %q pass %q", username, password, u, p)
|
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
return
|
|
} else {
|
|
// (at least one) request indicates authentication worked.
|
|
// The channel has a large buffer because many are queued before
|
|
// the receive instruction.
|
|
successCh <- true
|
|
}
|
|
|
|
// always delegate to the registry impl which implements the protocol
|
|
server.RegistryImpl.ServeHTTP(w, r)
|
|
}
|
|
defer server.Close()
|
|
|
|
// Client
|
|
// initialized with an OCI builder and pusher.
|
|
client := fn.New(
|
|
fn.WithBuilder(NewBuilder("", verbose)),
|
|
fn.WithPusher(NewPusher(false, false, verbose)))
|
|
|
|
// Function
|
|
// Built and tagged to push to the mock registry
|
|
f := fn.Function{
|
|
Root: root,
|
|
Runtime: "go",
|
|
Name: "f",
|
|
Registry: server.Addr().String() + "/funcs"}
|
|
|
|
if f, err = client.Init(f); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if f, err = client.Build(context.Background(), f); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Push
|
|
// Enables optional basic authentication via the push context to use instead
|
|
// of the default behavior of using the multi-auth chain of config files
|
|
// and various known credentials managers.
|
|
ctx := context.Background()
|
|
ctx = context.WithValue(ctx, fn.PushUsernameKey{}, username)
|
|
ctx = context.WithValue(ctx, fn.PushPasswordKey{}, password)
|
|
|
|
if _, _, err = client.Push(ctx, f); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
select {
|
|
case <-successCh:
|
|
case <-time.NewTimer(10 * time.Second).C:
|
|
t.Fatal("timed out waiting for a successful basic auth request")
|
|
}
|
|
}
|