- Mostly documenting properties of APIServer (for the most part Etcd has
the same properties).
- Adding executable examples, some off which run as additional test
cases.
You no longer have to pass a hostname to initialise the addressmanager.
The DefaultAddressManager always listens on localhost. If you want to
listen on some other interface, you can use a different AddressManager.
We now return an error when stopping of a process times out, before that
resulted in a panic. Now a caller of `Stop()` can catch an handle this
error.
Also, the timeouts for stopping and starting a process is now
configurable, for example by:
```
etcd := &test.APIServer{
StartTimeout: 12 * time.Second,
StopTimeout: 5 * time.Second,
}
```
In both Etcd and APIServer we return a descriptive Error when the
`URL()` method is called before `Start()` and thus the AddressManager is
not yet initialized.
Right now the ControlPlane is actually just a thin layer around
APIServer, this is the oly process we care for right now. Now that we
only have one process to start, we can remove the parallel starting
logic.
In case we bring in more processes again this commit can just be reverted.
We can move all of the logic out of the constructors and psuh them into
`ensureInitialized()` of both APIServer and Etcd.
By doing so, the constructors are actually not needed anymore.
We however kept the constructor for the ControlPlane for convinience.
The APIServer constructor previously required careful configuration. Now
it takes no arguments, and gives you an APIServer that you can
`.Start()`. If you want to configure it, you still can. For example, you
can set the environment variable `TEST_ASSET_KUBE_APISERVER` to the path
to your apiserver binary, or you can override the PathFinder in go code:
```
myAPIServer := test.NewAPIServer()
myAPIServer.PathFinder = func(_ string) string {
return "/path/to/my/apiserver/binary"
}
```
Previously the responsibility of choosing a port that the APIServer
could listen on was left to the caller. Now APIServer delegates that
responsibility to an AddressManager. By default you get a random unused
port on localhost. If you want to customize that behaviour, you can
overwrite the AddressManager:
```
myAPIServer := test.NewAPIServer()
myAPIServer.AddressManager = myAddressManager
```
If this is a common request, then in future we might provide some common
custom AddressManagers.
This means that if you want to customize the path to your etcd, instead
of doing `etcd.Path = "/my/path"` you should do:
```
etcd.PathFinder = func(_ string) string {
return "/my/path"
}
```
The advantage of this is that we move logic out of the constructor, so
we need less crazy dependancy injection logic in our tests, and we get
closer to being able to use the 0-value Etcd struct.
Everything now works pretty much like before, so we're not yet feeling a
lot of the benefit. Still to do:
- Remove all vestiges of Etcd config etc from the Fixtures struct
- Remove duplicated config
- Make Fixtures and APIServer constructors take 0 params
We now sanitize the binary names from which we construct environment
variables to query for custom binary paths. This means we can now
customize the apiserver binary path with $TEST_ASSET_KUBE_APISERVER
This is motivated by #162, but also involves changing the
NewFixtures(...) constructor which is the entry point to the whole
framework. We're removing the amount of config
you need to make it work, in line with #163.
- The default constructor for Etcd uses the DefaultBinPathFinder and the
default EtcdConfig constructor internally
- The Fixtures still use the old constructor, which means it passes in a
binary path and an EtcdConfig
- Remove the logic from the constructors
- Have start take a configuration map for the fixture processes
- Move the testing on open ports closer to the actual start
If $KUBE_ASSETS_DIR is set, we use that and try to run the binaries from
within that directory.
If it is not set, we try to determine the assets directory as a relative
path to the test suite.
The fixtures now exposes the URL the API Server is listening on. We can
get this with from `Fixtures.Config.APIServerURL`.
When we start our client program in the test, we pass that API Server
URL in via a command line flag.
- APIServer & Etcd get configured, from the outside, on which ports to
listen on
- Configuration, the subjects under test might be interested in, is
exposed by Fixtures.Config
Hint: Before we start any process, we get a random port and check if
that random port is acutally free to bind to. As it takes some time
until we actually start anything, we might run into cases, where another
process binds to that port while we are starting up. Even if we do the
port checking closer to actually binding, we still have the same issue.
For now, however, we take that risk - if we run into problems with that,
we are open to refactor that.
Eventually we want our framework to work nicely with just `go test`. To
get there we need to
- inject KUBE_ASSETS_DIR
- make the framework work when run multiple times in parallel (port
collitions, expose bound ports the the subject under test, ...)
We decided to make sure our tests are run in sequence (and not in
parallel to any other thing using etcd, for that matter) by making this
explicit in the `pre-commit.sh` - for now.
As soon as we are there, we can rollback the change to the
`pre-commit.sh` end have the test framework be tested the same as
everything else.
[#153248975]
While doing that we found that we needed to refactor the fakes to handle
command line arguments which are not known up front; we do this by using
regular expresseions.
We use the standard go client for kubernetes `client-go`. To vendor it
and all its denpendecies we use
```
dep ensure -add k8s.io/client-go@5.0.0
```
We create a new cobra command with
```
cobra add listPods
```
Note: The new command in cmd/listPods.go uses [the "magic" function
init()](https://golang.org/ref/spec#Package_initialization) to register
itself.
- Start() should only return when the process is actually up and
listening
- It may take some time to tear down a process, so we increased the
timeout for Stop() (to some random number)
- We make sure Std{Out,Err} is properly initialized, we should not rely
on Ginkgo/Gomega to do that for us
- Store stdout,stderr in private buffers
- Configure the etcURL on construction instead of at start time
- Handle the creation of the temporary directory (for the data
directory) internally
Testing the lifecycle of our fixtures with the real binaries. Test if we
can start the fixtures, the porcesses actually listen on the ports and
if we tear down all the parts successfully again.
We're not exercising the test framework yet, but it's in place.
Our democli expects its test assets to be in `./assets/bin`. We have a
script `./scripts/download-binaries.sh` which will populate that directory
from a google storage bucket.
Once those assets are in place, you can run tests with
`./scripts/run-tests.sh`.
Create a new set of test fixtures by doing:
```
f := test.NewFixtures("/path/to/etcd", "/path/to/apiserver")
```
Before running your integration tests, start all your fixtures:
```
err := f.Start()
Expect(err).NotTo(HaveOccurred())
```
Now that you have started your etcd and apiserver, you'll find the
apiserver listening locally on the default port. When you're done with
your testing, stop and clean up:
```
err := f.Stop()
Expect(err).NotTo(HaveOccurred())
```
To start an apiserver:
```
apiServer := APIServer{Path: "/path/to/my/apiserver/binary"}
session, err := apiServer.Start("tcp://whereever.is.my.etcd:port")
Expect(err).NotTo(HaveOccurred())
```
When you're done testing against that apiserver:
```
session.Terminate().Wait()
```
...or if you prefer:
```
gexec.Terminate()
```
...which will terminate not only this apiserver, but also all other
command sessions you started in this test.
We use [ginkgo](http://onsi.github.io/ginkgo/) and
[gomega](http://onsi.github.io/gomega/) for testing. We generate some
boilerplate with:
```
mkdir integration
cd integration
ginkgo bootstrap
ginkgo generate integration
```
We use
[gexec](http://onsi.github.io/gomega/#gexec-testing-external-processes)
to compile and run the CLI under test, and to inspect its output.
We use `dep ensure` to ensure that all our dependencies are properly
vendored. From now on, this will be our workflow with every commit.
We use [ginkgo](http://onsi.github.io/ginkgo/) and
[gomega](http://onsi.github.io/gomega/) for testing. We generate some
boilerplate with:
```
mkdir integration
cd integration
ginkgo bootstrap
ginkgo generate integration
```
We use
[gexec](http://onsi.github.io/gomega/#gexec-testing-external-processes)
to compile and run the CLI under test, and to inspect its output.
We use `dep ensure` to ensure that all our dependencies are properly
vendored. From now on, this will be our workflow with every commit.