mirror of https://github.com/knative/func.git
294 lines
7.1 KiB
Go
294 lines
7.1 KiB
Go
// +build integration
|
|
|
|
package function_test
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
boson "github.com/boson-project/func"
|
|
"github.com/boson-project/func/buildpacks"
|
|
"github.com/boson-project/func/docker"
|
|
"github.com/boson-project/func/knative"
|
|
)
|
|
|
|
/*
|
|
NOTE: Running integration tests locally requires a configured test cluster.
|
|
Test failures may require manual removal of dangling resources.
|
|
|
|
## Integration Cluster
|
|
|
|
These integration tests require a properly configured cluster,
|
|
such as that which is setup and configured in CI (see .github/workflows).
|
|
A local KinD cluster can be started via:
|
|
./hack/allocate.sh && ./hack/configure.sh
|
|
|
|
## Integration Testing
|
|
|
|
These tests can be run via the make target:
|
|
make test-integration
|
|
or manually by specifying the tag
|
|
go test -v -tags integration ./...
|
|
|
|
## Teardown and Cleanup
|
|
|
|
Tests should clean up after themselves. In the event of failures, one may
|
|
need to manually remove files:
|
|
rm -rf ./testdata/example.com
|
|
The test cluster is not automatically removed, as it can be reused. To remove:
|
|
./hack/delete.sh
|
|
*/
|
|
|
|
const (
|
|
// DefaultRegistry must contain both the registry host and
|
|
// registry namespace at this time. This will likely be
|
|
// split and defaulted to the forthcoming in-cluster registry.
|
|
DefaultRegistry = "localhost:5000/func"
|
|
|
|
// DefaultNamespace for the underlying deployments. Must be the same
|
|
// as is set up and configured (see hack/configure.sh)
|
|
DefaultNamespace = "func"
|
|
)
|
|
|
|
func TestList(t *testing.T) {
|
|
verbose := true
|
|
|
|
// Assemble
|
|
lister, err := knative.NewLister(DefaultNamespace)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
client := boson.New(
|
|
boson.WithLister(lister),
|
|
boson.WithVerbose(verbose))
|
|
|
|
// Act
|
|
names, err := client.List(context.Background())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Assert
|
|
if len(names) != 0 {
|
|
t.Fatalf("Expected no Functions, got %v", names)
|
|
}
|
|
}
|
|
|
|
// TestNew creates
|
|
func TestNew(t *testing.T) {
|
|
defer within(t, "testdata/example.com/testnew")()
|
|
verbose := true
|
|
|
|
client := newClient(verbose)
|
|
|
|
// Act
|
|
if err := client.New(context.Background(), boson.Function{Name: "testnew", Root: ".", Runtime: "go"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer del(t, client, "testnew")
|
|
|
|
// Assert
|
|
items, err := client.List(context.Background())
|
|
names := []string{}
|
|
for _, item := range items {
|
|
names = append(names, item.Name)
|
|
}
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(names, []string{"testnew"}) {
|
|
t.Fatalf("Expected function list ['testnew'], got %v", names)
|
|
}
|
|
}
|
|
|
|
// TestDeploy updates
|
|
func TestDeploy(t *testing.T) {
|
|
defer within(t, "testdata/example.com/deploy")()
|
|
verbose := true
|
|
|
|
client := newClient(verbose)
|
|
|
|
if err := client.New(context.Background(), boson.Function{Name: "deploy", Root: ".", Runtime: "go"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer del(t, client, "deploy")
|
|
|
|
if err := client.Deploy(context.Background(), "."); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestRemove deletes
|
|
func TestRemove(t *testing.T) {
|
|
defer within(t, "testdata/example.com/remove")()
|
|
verbose := true
|
|
|
|
client := newClient(verbose)
|
|
|
|
if err := client.New(context.Background(), boson.Function{Name: "remove", Root: ".", Runtime: "go"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
waitFor(t, client, "remove")
|
|
|
|
if err := client.Remove(context.Background(), boson.Function{Name: "remove"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
names, err := client.List(context.Background())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(names) != 0 {
|
|
t.Fatalf("Expected empty Functions list, got %v", names)
|
|
}
|
|
}
|
|
|
|
// ***********
|
|
// Helpers
|
|
// ***********
|
|
|
|
// newClient creates an instance of the func client whose concrete impls
|
|
// match those created by the kn func plugin CLI.
|
|
func newClient(verbose bool) *boson.Client {
|
|
builder := buildpacks.NewBuilder()
|
|
builder.Verbose = verbose
|
|
|
|
pusher, err := docker.NewPusher()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
pusher.Verbose = verbose
|
|
|
|
deployer, err := knative.NewDeployer(DefaultNamespace)
|
|
if err != nil {
|
|
panic(err) // TODO: remove error from deployer constructor
|
|
}
|
|
deployer.Verbose = verbose
|
|
|
|
remover, err := knative.NewRemover(DefaultNamespace)
|
|
if err != nil {
|
|
panic(err) // TODO: remove error from remover constructor
|
|
}
|
|
remover.Verbose = verbose
|
|
|
|
lister, err := knative.NewLister(DefaultNamespace)
|
|
if err != nil {
|
|
panic(err) // TODO: remove error from lister constructor
|
|
}
|
|
lister.Verbose = verbose
|
|
|
|
return boson.New(
|
|
boson.WithRegistry(DefaultRegistry),
|
|
boson.WithVerbose(verbose),
|
|
boson.WithBuilder(builder),
|
|
boson.WithPusher(pusher),
|
|
boson.WithDeployer(deployer),
|
|
boson.WithRemover(remover),
|
|
boson.WithLister(lister),
|
|
)
|
|
}
|
|
|
|
// Del cleans up after a test by removing a function by name.
|
|
// (test fails if the named function does not exist)
|
|
//
|
|
// Intended to be run in a defer statement immediately after creation, del
|
|
// works around the asynchronicity of the underlying platform's creation
|
|
// step by polling the provider until the names function becomes available
|
|
// (or the test times out), before firing off a deletion request.
|
|
// Of course, ideally this would be replaced by the use of a synchronous
|
|
// method, or at a minimum a way to register a callback/listener for the
|
|
// creation event. This is what we have for now, and the show must go on.
|
|
func del(t *testing.T, c *boson.Client, name string) {
|
|
t.Helper()
|
|
waitFor(t, c, name)
|
|
if err := c.Remove(context.Background(), boson.Function{Name: name}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// waitFor the named Function to become available in List output.
|
|
// TODO: the API should be synchronous, but that depends first on
|
|
// Create returning the derived name such that we can bake polling in.
|
|
// Ideally the Boson provider's Creaet would be made syncrhonous.
|
|
func waitFor(t *testing.T, c *boson.Client, name string) {
|
|
t.Helper()
|
|
var pollInterval = 2 * time.Second
|
|
|
|
for { // ever (i.e. defer to global test timeout)
|
|
nn, err := c.List(context.Background())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, n := range nn {
|
|
if n.Name == name {
|
|
return
|
|
}
|
|
}
|
|
time.Sleep(pollInterval)
|
|
}
|
|
}
|
|
|
|
// Create the given directory, CD to it, and return a function which can be
|
|
// run in a defer statement to return to the original directory and cleanup.
|
|
// Note must be executed, not deferred itself
|
|
// NO: defer within(t, "somedir")
|
|
// YES: defer within(t, "somedir")()
|
|
func within(t *testing.T, root string) func() {
|
|
t.Helper()
|
|
cwd := pwd(t)
|
|
mkdir(t, root)
|
|
cd(t, root)
|
|
return func() {
|
|
cd(t, cwd)
|
|
rm(t, root)
|
|
}
|
|
}
|
|
|
|
func pwd(t *testing.T) string {
|
|
t.Helper()
|
|
dir, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return dir
|
|
}
|
|
|
|
func mkdir(t *testing.T, dir string) {
|
|
t.Helper()
|
|
if err := os.MkdirAll(dir, 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func cd(t *testing.T, dir string) {
|
|
t.Helper()
|
|
if err := os.Chdir(dir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func rm(t *testing.T, dir string) {
|
|
t.Helper()
|
|
if err := os.RemoveAll(dir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func touch(file string) {
|
|
_, err := os.Stat(file)
|
|
if os.IsNotExist(err) {
|
|
f, err := os.Create(file)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer f.Close()
|
|
}
|
|
t := time.Now().Local()
|
|
if err := os.Chtimes(file, t, t); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|