-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Test verify subcommand in Go end-to-end #185
Conversation
f0591ab
to
f51edc1
Compare
ce7aaa8
to
0779720
Compare
0779720
to
e24c192
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise looks good to me. 🦭
e2e/openssl/openssl_test.go
Outdated
output, err := os.MkdirTemp("", "nunki-verify.*") | ||
require.NoError(err) | ||
t.Cleanup(func() { | ||
_ = os.RemoveAll(output) | ||
}) | ||
|
||
coordinator, cancelPortforward, err := c.PortForwardPod(ctx, namespace, "port-forwarder-coordinator", "1313") | ||
require.NoError(err) | ||
t.Cleanup(cancelPortforward) | ||
|
||
verify := cmd.NewVerifyCmd() | ||
verify.SetArgs([]string{ | ||
"--output", output, | ||
"--coordinator-policy-hash=", // TODO(burgerdev): enable policy checking | ||
"--coordinator", coordinator, | ||
}) | ||
verify.SetOut(io.Discard) // TODO: do we need it? | ||
errBuf := &bytes.Buffer{} | ||
verify.SetErr(errBuf) | ||
|
||
if err := verify.Execute(); err != nil { | ||
t.Log(string(errBuf.Bytes())) | ||
t.Fatalf("could not verify coordinator: %v", err) | ||
} | ||
|
||
for _, expected := range []string{"manifest.0.json", "coordinator-root.pem", "mesh-root.pem"} { | ||
_, err := os.Stat(path.Join(output, expected)) | ||
assert.NoError(err, "expected verify output to contain file %q", expected) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say this shouldn't be part of the OpenSSL test. We should rather create a test suite in TestMain or at least run subtests in this within this test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved this into a TestMain, but then realized that there's a bunch of duplication between that and the OpenSSL test proper, so I decided to restructure the Kubeclient a bit.
- It now owns the k8s namespace (in anticipation of being responsible for creating it later).
- It exposes a Setup/Teardown pair, to be called by the TestMain.
- Nunki verify (and later apply and set) are part of Setup (and cleaned up in Teardown).
A bit ugly is how the kubeclient is passed to TestOpenssl (via global var).
e24c192
to
4928d38
Compare
4928d38
to
9a5693d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good Job! the overall architecture looks very usable.
@@ -30,6 +30,13 @@ type Kubeclient struct { | |||
client *kubernetes.Clientset | |||
// config is the "Kubeconfig" for the client | |||
config *rest.Config | |||
|
|||
// Below fields are only populated by Setup(). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this makes the Kubeclient a bit too stateful. At least we should think about separating the Kubernetes parts from the Contrast/testing parts.
Generally, I think it's fine to hold the namespace state at e.g. the testing level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion, it's probably for the better to split these up.
e2e/internal/kubeclient/setup.go
Outdated
// | ||
// If any setup step fails, Setup returns an error but does not clean up any resources. Call | ||
// Teardown for that. | ||
func (k *Kubeclient) Setup() error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we want to assert the behavior of the Nunki steps contained in here, this should likely be part of the test code. Also this makes more sense, since this code is purpose built for the e2e tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the other hand, this is the same for all test variants (simple, openssl, enojivoto), and at least set
is a prerequisite for running the tests at all. Maybe my idea of putting all of just default
into Setup is wrong, and it should rather be Setup and a dedicated test for verify. Let me try to rework that.
e2e/openssl/openssl_test.go
Outdated
// namespace the tests are executed in. | ||
const namespaceEnv = "K8S_NAMESPACE" | ||
// filled by TestMain | ||
var k *kubeclient.Kubeclient |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that we need global state in the test. Sure, there is sort of global state since we are re-using on AKS, but by using different e.g. namespaces we can keep the tests independent (and also execute them conurrently if we were to scale up the AKS)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have global state, in the sense that the coordinator is shared between tests and set up/torn down in the TestMain (see also @katexochen's requested changes). Sharing the kubeclient just seemed like a convenient way of sharing that state, but I agree that it's less obvious what state is shared.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Talked to Paul and convinced him that it's not a good idea to generally share the coordinator between tests. The reasoning is mostly that the emojivoto test should be completely independent of the openssl test.
Of course we want to re-use the coordinator for (sub-)tests of the same functionality, i.e. 1. Test is calling set and asserting the certs and the 2. test calls set again and also checks that the new mesh certs are not singed by the same Mesh/IntermRoot.
This creates some overhead, mostly in the memory the coordinator is using so we likely want to call those test with --parallel=1
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This creates some overhead, mostly in the memory the coordinator is using so we likely want to call those test with --parallel=1.
Test and subtests in the same package should run sequentially if they don't call t.Parallel()
. The rest can be done using the sync server (soon™).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True for subtests, but I don't think execution order is specified for top-level tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No order isn't specified, and tests should not rely on order. But still won't run in parallel.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, according to what I found top-level test are run in separate go go routines which execution can overlap. I wasn't sure if we put all e2e tests in the same package, therefore the hint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was reading @3u13r's proposal as having set
, verify
and specific tests in separate Test functions on the same level, which only works if execution order is guaranteed. Maybe I got that wrong. But we can have ordered subtests to that:
func TestOpenSSL(t *testing.T) {
c := kubeclient.NewForTest(t)
// Generate and apply YAML
cli := nunkitest.New(t)
t.Run("set", cli.set)
t.Run("verify", cli.verify)
t.Run("openssl-specifics", func(t *testing.T) {
// exec into backend, etc.
})
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe even:
func TestBasicCommands(t *testing.T) {
c := kubeclient.NewForTest(t)
// Generate and apply YAML
cli := nunkitest.New(t)
t.Run("set", cli.set)
t.Run("verify", cli.verify)
// Teardown
}
func TestOpenSSL(t *testing.T) {
c := kubeclient.NewForTest(t)
// Generate and apply YAML
cli := nunkitest.New(t)
t.Run("set", cli.set)
t.Run("openssl test a", func(t *testing.T) {
// exec into backend, etc.
})
t.Run("openssl test b", func(t *testing.T) {
// exec into backend, etc.
})
// Teardown
}
The most important part here is that we get individual feedback from different tests, even if some of them fail. I'm not that sure about the grouping in this case, but we will for sure have tests that should be executed against a separate coordinator, so it might be a good idea to do that for the supported tests now so it is part of our test structure already.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, thanks for the discussion everybody. While I revisit that, I would like to advertise a spin off at #191 that should be less contentious and would help me keep things focused here.
1e1a190
to
8937588
Compare
I rewrote this PR to focus on the use of
|
8937588
to
72dfea9
Compare
This commit implements a first use case for calling the CLI commands from e2e test code. It is deliberately kept inline to focus on the test mechanics first. Once we need to call verify from more places, we can factor it into a test helper library. Co-authored-by: Paul Meyer <[email protected]>
01a39d5
to
ae08e4d
Compare
No description provided.