== Requirements Module

Arquillian Cube tries to connect to the local Docker installation, or to a Kubernetes or OpenShift cluster.

Sometimes the test environment does not support one of the above-mentioned targets, and some tests may fail, e.g.: a
test requiring OpenShift 4 would fail if the test environment does not support providing an OpenShift 4 cluster.

Obviously this is not a test failure, and test should rather be ignored.
For this use case and similar ones, Arquillian Cube provides the *requirements* module.
This module makes it easy to skip tests, instead of failing, in the case any environment expectations are not met.

=== Example of environment requirements

By using _Requirements_, you can set if you want to skip tests based on variable criteria, like missing environment
variables or system properties.
This is useful for example if you require that `DOCKER_HOST`, `DOCKER_TLS_VERIFY` or `DOCKER_CERT_PATH` system or
environment variable must be set.
Similarly, for example in the Kubernetes use case, a requirement can prevent a test from being executed if the cluster
is not available.

Notice that Cube gives precedence to these variables, as to those configured in `arquillian.xml`.

To use it you need to add requirements dependency.

[source, xml]
.pom.xml
----
<dependency>
    <groupId>org.arquillian.cube</groupId>
    <artifactId>arquillian-cube-requirement</artifactId>
    <scope>test</scope>
</dependency>
----

Then you can use a special runner or a JUnit _Rule_.
If you use a _Rule_, the scope of annotations can only be a test class, while annotations can be used at the suite level
when using an _ArquillianConditionalRunner_.

Let's see how to use with JUnit _Rule_.

[source, java]
----
import org.arquillian.cube.requirement.RequiresSystemPropertyOrEnvironmentVariable;
import org.arquillian.cube.requirement.RequirementRule;

@RunWith(Arquillian.class)
@RequiresSystemPropertyOrEnvironmentVariable(value = {"DOCKER_HOST"}) // <1>
public class HelloWorldServletTest {

    @Rule // <2>
    public RequirementRule requirementRule = new RequirementRule();

    //....

}
----
<1> Checks if either a system property or an environment variable with name `DOCKER_HOST` is set
<2> Rule definition

Alternatively, you can use the _ArquillianConditionalRunner_ approach to use the requirement at the suite level.

[source, java]
----
import org.arquillian.cube.requirement.RequiresSystemPropertyOrEnvironmentVariable;
import org.arquillian.cube.requirement.ArquillianConditionalRunner;

@RunWith(ArquillianConditionalRunner.class) // <1>
@RequiresSystemPropertyOrEnvironmentVariable(value = {"DOCKER_HOST"}) // <2>
public class HelloWorldServletTest {

    //....

}
----
<1> Runner for requirements check
<2> Checks if either a system property or an environment variable with name `DOCKER_HOST` is set

The `RequiresEnvironmentVariable` and `RequiresSystemProperty` annotations can be used too, which respectively check
whether _just_ an environment variable or a system property ar set.

=== Example with OpenShift 4

The `openshift` module also defines an annotation for checking the environment against an OpenShift 4 cluster existence.

[source, java]
----

import org.arquillian.cube.requirement.ArquillianConditionalRunner;
import org.arquillian.cube.openshift.impl.requirement.RequiresOpenShift4;

@RunWith(ArquillianConditionalRunner.class)
@RequiresOpenshift4 // <1>
public class RouteInOtherNamespaceIT {

    //....
}
----
<1> OpenShift 4 is required for the test to be executed

The test will only be executed if an OpenShift 4 cluster is available in the target environment

=== Customizing Requirements

You can also implement your own requirement annotations.
To do it you only need to do two things.

* Add an annotation interface, decorated with `org.arquillian.cube.spi.requirement.Requires` pointing to a class
which implements `org.arquillian.cube.spi.requirement.Requirement`.
* Add an implementation of the `org.arquillian.cube.spi.requirement.Requirement` interface.

Let's see an example of how to implement a requirement against docker version.

[source, java]
.RequiresDockerVersion.java
----
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Requires(DockerRequirement.class) // <1>
public @interface RequiresDockerVersion {
    String name() default ""; // <2>
}
----
<1> Sets requirement interface
<2> Attribute to set the required version

And the implementation logic:

[source, java]
.DockerRequirement.java
----
public class DockerRequirement implements Requirement<RequiresDockerVersion> {

    @Override
    public void check(RequiresDocker context) throws UnsatisfiedRequirementException {
        try {
            Map<String, String> config = resolver.resolve(new HashMap<String, String>());
            String serverUrl = config.get(CubeDockerConfiguration.DOCKER_URI);
            if (Strings.isNullOrEmpty(serverUrl)) {
                throw new UnsatisfiedRequirementException("Could not resolve the docker server url."); // <1>
            } else if (!isDockerRunning(serverUrl)) {
                throw new UnsatisfiedRequirementException("No server is running on url:[" + serverUrl + "].");
            }
        } catch (ExecutionException e) {
            throw new UnsatisfiedRequirementException("Cannot execute docker command.");
        }
    }
}
----
<1> In case of not meeting an expectation, `org.arquillian.cube.spi.requirement.UnsatisfiedRequirementException` should be
thrown with a message.

After that you can use this annotation as any other requirements provided by Cube.
