open-nomad/e2e/v3/namespaces3/namespaces3.go
Seth Hoenig 2c7877658c
e2e: create a v3/ set of packages for creating Nomad e2e tests (#17620)
* e2e: create a v3/ set of packages for creating Nomad e2e tests

This PR creates an experimental set of packages under `e2e/v3/` for crafting
Nomad e2e tests. Unlike previous generations, this is an attempt at providing
a way to create tests in a declarative (ish) pattern, with a focus on being
easy to use, easy to cleanup, and easy to debug.

@shoenig is just trying this out to see how it goes.

Lots of features need to be implemented.
Many more docs need to be written.
Breaking changes are to be expected.
There are known and unknown bugs.
No warranty.

Quick run of `example` with verbose logging.

```shell
➜ NOMAD_E2E_VERBOSE=1 go test -v
=== RUN   TestExample
=== RUN   TestExample/testSleep
    util3.go:25: register (service) job: "sleep-809"
    util3.go:25: checking eval: 9f0ae04d-7259-9333-3763-44d0592d03a1, status: pending
    util3.go:25: checking eval: 9f0ae04d-7259-9333-3763-44d0592d03a1, status: complete
    util3.go:25: checking deployment: a85ad2f8-269c-6620-d390-8eac7a9c397d, status: running
    util3.go:25: checking deployment: a85ad2f8-269c-6620-d390-8eac7a9c397d, status: running
    util3.go:25: checking deployment: a85ad2f8-269c-6620-d390-8eac7a9c397d, status: running
    util3.go:25: checking deployment: a85ad2f8-269c-6620-d390-8eac7a9c397d, status: running
    util3.go:25: checking deployment: a85ad2f8-269c-6620-d390-8eac7a9c397d, status: successful
    util3.go:25: deployment a85ad2f8-269c-6620-d390-8eac7a9c397d was a success
    util3.go:25: deregister job "sleep-809"
    util3.go:25: system gc
=== RUN   TestExample/testNamespace
    util3.go:25: apply namespace "example-291"
    util3.go:25: register (service) job: "sleep-967"
    util3.go:25: checking eval: a2a2303a-adf1-2621-042e-a9654292e569, status: pending
    util3.go:25: checking eval: a2a2303a-adf1-2621-042e-a9654292e569, status: complete
    util3.go:25: checking deployment: 3395e9a8-3ffc-8990-d5b8-cc0ce311f302, status: running
    util3.go:25: checking deployment: 3395e9a8-3ffc-8990-d5b8-cc0ce311f302, status: running
    util3.go:25: checking deployment: 3395e9a8-3ffc-8990-d5b8-cc0ce311f302, status: running
    util3.go:25: checking deployment: 3395e9a8-3ffc-8990-d5b8-cc0ce311f302, status: successful
    util3.go:25: deployment 3395e9a8-3ffc-8990-d5b8-cc0ce311f302 was a success
    util3.go:25: deregister job "sleep-967"
    util3.go:25: system gc
    util3.go:25: cleanup namespace "example-291"
=== RUN   TestExample/testEnv
    util3.go:25: register (batch) job: "env-582"
    util3.go:25: checking eval: 600f3bce-ea17-6d13-9d20-9d9eb2a784f7, status: pending
    util3.go:25: checking eval: 600f3bce-ea17-6d13-9d20-9d9eb2a784f7, status: complete
    util3.go:25: deregister job "env-582"
    util3.go:25: system gc
--- PASS: TestExample (10.08s)
    --- PASS: TestExample/testSleep (5.02s)
    --- PASS: TestExample/testNamespace (4.02s)
    --- PASS: TestExample/testEnv (1.03s)
PASS
ok      github.com/hashicorp/nomad/e2e/example  10.079s
```

* cluster3: use filter for kernel.name instead of filtering manually
2023-06-23 09:10:49 -05:00

167 lines
3.5 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package namespaces3
import (
"testing"
"time"
"github.com/hashicorp/go-set"
nomadapi "github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/e2e/v3/util3"
"github.com/hashicorp/nomad/helper"
"github.com/shoenig/test"
"github.com/shoenig/test/must"
)
type Names struct {
t *testing.T
nomadClient *nomadapi.Client
noCleanup bool
timeout time.Duration
verbose bool
apply *set.HashSet[*Namespace, string]
remove *set.Set[string]
}
func (g *Names) logf(msg string, args ...any) {
util3.Log3(g.t, g.verbose, msg, args...)
}
func (g *Names) cleanup() {
if g.noCleanup {
return
}
namespaceAPI := g.nomadClient.Namespaces()
// remove any namespaces we created (or updated)
for _, namespace := range g.apply.Slice() {
name := namespace.Name
g.logf("cleanup namespace %q", name)
_, err := namespaceAPI.Delete(name, nil)
test.NoError(g.t, err, test.Sprintf("unable to delete namespace %q", name))
}
}
type Option func(*Names)
type Cleanup func()
type Namespace struct {
Name string
Description string
}
func (ns *Namespace) Hash() string {
return ns.Name
}
func (ns *Namespace) String() string {
return ns.Name
}
func (n *Names) setClient() {
nomadClient, nomadErr := nomadapi.NewClient(nomadapi.DefaultConfig())
must.NoError(n.t, nomadErr, must.Sprint("failed to create nomad api client"))
n.nomadClient = nomadClient
}
func configure(t *testing.T, opts ...Option) Cleanup {
g := &Names{
t: t,
timeout: 10 * time.Second,
apply: set.NewHashSet[*Namespace, string](3),
remove: set.New[string](3),
}
for _, opt := range opts {
opt(g)
}
g.setClient()
g.run()
return g.cleanup
}
func (g *Names) run() {
namespacesAPI := g.nomadClient.Namespaces()
// do deletions
for _, namespace := range g.remove.Slice() {
g.logf("delete namespace %q", namespace)
_, err := namespacesAPI.Delete(namespace, nil)
must.NoError(g.t, err)
}
// do applies
for _, namespace := range g.apply.Slice() {
g.logf("apply namespace %q", namespace)
_, err := namespacesAPI.Register(&nomadapi.Namespace{
Name: namespace.Name,
Description: namespace.Description,
}, nil)
must.NoError(g.t, err)
}
}
// Create a namespace of the given name.
func Create(t *testing.T, name string, opts ...Option) Cleanup {
namespace := &Namespace{Name: name}
opt := apply(namespace)
return configure(t, append(opts, opt)...)
}
// Create namespaces of the given names.
func CreateN(t *testing.T, names []string, opts ...Option) Cleanup {
creations := helper.ConvertSlice(names, func(name string) Option {
namespace := &Namespace{Name: name}
return apply(namespace)
})
return configure(t, append(opts, creations...)...)
}
// Delete the namespace of the given name.
func Delete(t *testing.T, name string, opts ...Option) Cleanup {
opt := remove(name)
return configure(t, append(opts, opt)...)
}
func apply(namespace *Namespace) Option {
return func(g *Names) {
g.apply.Insert(namespace)
}
}
func remove(name string) Option {
return func(g *Names) {
g.remove.Insert(name)
}
}
// DisableCleanup will disable the automatic removal of any namespaces
// created using the Create() Option.
func DisableCleanup() Option {
return func(n *Names) {
n.noCleanup = true
}
}
func Timeout(timeout time.Duration) Option {
return func(n *Names) {
n.timeout = timeout
}
}
// Verbose will enable verbose logging.
func Verbose(on bool) Option {
return func(n *Names) {
n.verbose = on
}
}