arquillian / arquillian-cube

Control (docker, kubernetes, openshift) containers in your tests with ease!
http://arquillian.org/arquillian-cube/
121 stars 98 forks source link

How to use Cube outside of Arquillian Test? #242

Open smiklosovic opened 8 years ago

smiklosovic commented 8 years ago

@lordofthejars @aslakknutsen

This is rather question then an "issue".

I would love to use Cube but the thing what I try to achieve is that I need to manage containers with some neat Java API and once containers are started, I have another project which just executes tests against some URL. And I get that URL after container is started so I need to separate the container creation from the test execution.

I need, more or less, to control containers from Arquillian Spacelift Gradle plugin where, e.g. in "beforeTest" / "beforeSuite" closure, I will start a container with Cube API and get some manager object and after tests are executed, I need to stop that container e.g. in "afterTest" or "afterSuite" by that manager.

Is this possible? Can I just depend on some artifact via which I would construct and start Docker container, providing some docker-compose.yml? In my usecase, I do not need to have this all stuff embedded and wired directly in some Arquillian-enabled test because my tests are already lying somewhere else and are not Arquillian-enabled and likely will never be. I do not need to use Arquillian runtime for this.

aslakknutsen commented 8 years ago

Sounds like you want Cube- standalone.

On Sat, Jan 9, 2016, 12:23 Štefan Miklošovič notifications@github.com wrote:

@lordofthejars https://github.com/lordofthejars @aslakknutsen https://github.com/aslakknutsen

This is rather question then an "issue"

I would love to use Cube but the thing what I try to achieve is that I need to manage containers with some neat Java API and once containers are started, I have another project which just execute tests against some URL And I get that URL after container is stared so I need to separate the container creation from the test execution

I need, more or less, to controll containers from Arquillian Spacelift Gradle plugin where, eg in "beforeTest" / "beforeSuite" closure, I will start a container with Cube API and get some manager object and after tests are executed, I need to stop that container eg in "afterTest" or "afterSuite"

Is this possible? Can I just depend on some artifact via which I would construct and start Docker container, providing some docker-composeyml? My my usecase, I do not need to have this all stuff embedded and wired directly in some Arquillian-enabled test I do not need to use Arquillian runtime for this

— Reply to this email directly or view it on GitHub https://github.com/arquillian/arquillian-cube/issues/242.

smiklosovic commented 8 years ago

If you mean this (1) it uses injected CubeController to manage them. I can not obviously inject anything and it uses some events to be fired beneath and in my scenario I will never have them injected in the first place.

I think it could be possible to use DockerClientExecutor class direcly. It does not depend on anything Arquillian-specific and it looks like I can safely use it directly.

Is there another approach how I can use Cube's internals to embed its functionality into something completely different?

https://github.com/arquillian/arquillian-cube/blob/master/docker/ftest-standalone/src/test/java/org/arquillian/cube/StandaloneTestCase.java

lordofthejars commented 8 years ago

Hi @smiklosovic for what I see I think this is a use case we are missing in Cube. I agree with you that using standalone mode would not require manual steps for starting the containers. For what I see, when starting a standalone, it should take the orchestration file (docker-compose), analyze it and start everything in the correct order. So you only need to set the Runner to be Arquillan and that's all. Something like it is done in containerless.

I think it has sense.

aslakknutsen commented 8 years ago

It's not very hard to get it running outside of a Test..

This is based on the 'docker/ftest-standalone' classpath.

Manager manager = ManagerBuilder.from()
                       .extension(LoadableExtensionLoader.class)
                       .create();

manager.start();

String cubeId = "database";

manager.fire(new CreateCube(cubeId));
manager.fire(new StartCube(cubeId));

CubeRegistry registry = manager.resolve(CubeRegistry.class);
Cube<?> cube = registry.getCube(cubeId);

System.out.println("Cube");
System.out.println("ip:          " + cube.bindings().getIP());
System.out.println("bound ports: " + cube.bindings().getNumberOfPortBindings());

manager.fire(new StopCube(cubeId));
manager.fire(new DestroyCube(cubeId));

manager.shutdown();

In this case arquillian.xml is read. But you could provide your own ArquillianDescriptor provider or just set system properties before starting. All values in arquillian.xml can be replaced / added via properties.

aslakknutsen commented 8 years ago

Technically you could do this as well, as far as Cube is concerned, but you might miss out on some other extensions that react to the events(tho other extensions should listen to Before|After Create|Start|Stop|Destory so maybe it doesn't matter as those are fired in both cases):

String cubeId = "database";
CubeRegistry registry = manager.resolve(CubeRegistry.class);
Cube<?> cube = registry.getCube(cubeId);

cube.create();
cube.start();

System.out.println("Cube");
System.out.println("ip:          " + cube.bindings().getIP());
System.out.println("bound ports: " + cube.bindings().getNumberOfPortBindings());

cube.stop();
cube.destroy();
aslakknutsen commented 8 years ago

You could do mamager.resolve(CubeController.class) as well, but you'd still need the Cube to get to the binding data. So not sure CubeController would do any good in this case.

smiklosovic commented 8 years ago

Awesome hint Aslak. I will look into this. I can imagine this could be wrapped in some helper class so it would be a no-brainer. I think that you could use this in test case itself but it would not be run with arquillian runner but with normal JUnit4.class ....

aslakknutsen commented 8 years ago

Yeah, that's what I'm doing here:

public class ManualStartTest {

    @Test
    public void should() throws Exception {

        Manager manager = ManagerBuilder.from()
...

How else do you run code? ;)

smiklosovic commented 8 years ago

:) This just begs for @ClassRulling it ...

aslakknutsen commented 8 years ago

@smiklosovic so why not use the Arquillian runner? :)

smiklosovic commented 8 years ago

There are some cucumber + serenity tests and it has some crazy runner and even I know there is some Cuke + Arquillian integration, it would mean that the structure of the tests would have to change significantly to "Arquillianify it" and nobody aint got time for that. Plus there is not Serenity support in that extension...

It seems to me that better approach would be just to extract the functionality of what I need when it comes to Docker and set it up in some scenario / steps fixtures.

Extracting into separate artifact would enable reusability e.g. in that arquillian-spacelift-gradle plugin as well where I could use it in before / after test closures. Having the possibility to use Rule would be just a nice addon where you could eventually run tests against your local setup or against docker if you want to without Arquillian runner but with Arquillian internals from Arquillian-Docker core.

aslakknutsen commented 8 years ago

It's only 3 lines of code to get Arquillian created/started/stopped. The rest is Cube already. Not sure what you would gain by 'extract the functionality of what I need when it comes to Docker' .

smiklosovic commented 8 years ago

Yes, maybe I am overengineering here little bit. I think that, looking at your code you have posted while ago, it would be sufficient to not wrap it into anything and use it directly as-is.

smiklosovic commented 8 years ago

@aslakknutsen it works and it is awesome :)

aslakknutsen commented 8 years ago

@smiklosovic excellent! :+1:

aslakknutsen commented 8 years ago

So technically possible to do, I think we still need to document it to show how. So we can leave this open as a doc issue. wdyt?

smiklosovic commented 8 years ago

https://gist.github.com/smiklosovic/efbf59bdddac725d9524

aslakknutsen commented 8 years ago

@smiklosovic nice! Why the custom extension loading ? https://gist.github.com/smiklosovic/efbf59bdddac725d9524#file-dockermanager-java-L124

smiklosovic commented 8 years ago

If I recall correctly, without that, ArquillianDescriptor was not resolved nor fired further to the Cube extension,.

smiklosovic commented 8 years ago

Huh ... it is working without that line as well, even better, that was some kind of fuzz ... However config-imp-base still has to be there

aslakknutsen commented 8 years ago

yeah, I assume it came in before you added config-impl to cp :) Shouldn't need the custom reg of CubeDockerExtension either. It's not a @Observer in the scense of Core anyway so it won't do anything :)

smiklosovic commented 8 years ago

confirmed, thanks

I am wondering what about Docker compose scenario. I have docker-compose.yml with custom images deployed in private Docker registry. docker-compose up works ok. I am just starting one container of them in the test right now but having them all started would be cool as well.

I had to specify cubeId which was equal to name in dockerContainers property. However when I set dockerContainersFile to compose file, what cubeId should I use?

Should I just start them one by one using name of container as cubeId? Once container is linked to other three, is not it sufficient to start just that one and all linked containers would be started as well?

smiklosovic commented 8 years ago

I do not know who wrote that Cube readme but I bet on Alex and I have to say that it reads like a wine. Every now and then I "hmmm" for myself getting know something new. Kudos to @lordofthejars !

aslakknutsen commented 8 years ago

You could try to set autoStartContainers to regexp:.* or something.. that should start all containers.

smiklosovic commented 8 years ago

@aslakknutsen

It is possible to use this in a suite?

My problem is that I have JUnit Categories tests and I gather all Docker-like tests together. Every test case class interacts with Docker somehow.

I have Manager static and the funny thing is that when Manager is shutted down (as a result of the test case itself, I am shutting down Manager after all containers are stopped), it throws NPE on the run of the other test class, contexts array is clean so ApplicationScoped context is null so it can not fire events and so on ...

Any hint?

smiklosovic commented 8 years ago

I slightly rewrote that class in a way that it constructs Manager (= loads the extension) everytime it detects that it is stopped so it effectively activates application context.

aslakknutsen commented 8 years ago

@smiklosovic If you can find the correct hooks sure. @Before / @After in a @Runner(Suite.class) executes before and after the suite if I remember correctly.

aslakknutsen commented 8 years ago

But you'll need to enforce the Junit4.7 Maven Surefire runner to support it, and I believe exclude the 'Suite children' from running in Maven. And you'll need to ru the Suite for any of them to work, as a Child doesn't know of the Suite but it requires it.. I guess you could workaround that with some Base class the Docker ones and the Suite extend and store Manager in a static or something to only initiate it once (on second thought, you'l still have the After / Manager closed issue with the Base class idea)