This is an automated email from the ASF dual-hosted git repository. ppalaga pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
commit 9788fb651291473547b20f6ff5f70f01b221a631 Author: Zineb Bendhiba <bendhiba.zi...@gmail.com> AuthorDate: Mon Mar 15 17:27:11 2021 +0100 DigitalOcean native support #1594 --- .../pages/reference/extensions/digitalocean.adoc | 14 +- .../reference/components/digitalocean.adoc | 6 +- .../deployment/DigitaloceanProcessor.java | 46 ---- .../digitalocean/it/DigitaloceanResource.java | 51 ----- extensions-jvm/pom.xml | 1 - .../digitalocean/deployment/pom.xml | 14 ++ .../deployment/DigitaloceanProcessor.java | 60 ++++++ .../digitalocean/pom.xml | 1 - .../digitalocean/runtime/pom.xml | 9 + .../main/resources/META-INF/quarkus-extension.yaml | 3 +- extensions/pom.xml | 1 + .../WireMockTestResourceLifecycleManager.java | 9 +- integration-tests/digitalocean/README.adoc | 13 ++ integration-tests/digitalocean/pom.xml | 158 ++++++++++++++ .../digitalocean/it/DigitalOceanClientMock.java | 18 +- .../digitalocean/it/DigitaloceanResource.java | 237 +++++++++++++++++++++ .../digitalocean/it/DigitaloceanRoute.java | 60 ++++++ .../component/digitalocean/it/MockApiService.java | 22 +- .../component/digitalocean/it/DigitaloceanIT.java | 16 +- .../digitalocean/it/DigitaloceanTest.java | 231 ++++++++++++++++++++ .../digitalocean/it/DigitaloceanTestResource.java | 22 +- .../test/resources/mappings/actionsDroplet.json | 28 +++ .../test/resources/mappings/backupsDroplet.json | 28 +++ .../resources/mappings/createMultipleDroplets.json | 28 +++ .../src/test/resources/mappings/deleteDroplet.json | 28 +++ .../src/test/resources/mappings/dropletAction.json | 28 +++ .../src/test/resources/mappings/getDroplet.json | 28 +++ .../test/resources/mappings/kernelsDroplet.json | 28 +++ .../test/resources/mappings/neighborsDroplet.json | 28 +++ .../test/resources/mappings/snapshotsDroplet.json | 28 +++ integration-tests/pom.xml | 1 + tooling/scripts/test-categories.yaml | 1 + 32 files changed, 1090 insertions(+), 156 deletions(-) diff --git a/docs/modules/ROOT/pages/reference/extensions/digitalocean.adoc b/docs/modules/ROOT/pages/reference/extensions/digitalocean.adoc index 7855078..60aa2ba 100644 --- a/docs/modules/ROOT/pages/reference/extensions/digitalocean.adoc +++ b/docs/modules/ROOT/pages/reference/extensions/digitalocean.adoc @@ -2,15 +2,15 @@ // This file was generated by camel-quarkus-maven-plugin:update-extension-doc-page = DigitalOcean :cq-artifact-id: camel-quarkus-digitalocean -:cq-native-supported: false -:cq-status: Preview +:cq-native-supported: true +:cq-status: Stable :cq-description: Manage Droplets and resources within the DigitalOcean cloud. :cq-deprecated: false :cq-jvm-since: 1.1.0 -:cq-native-since: n/a +:cq-native-since: 1.8.0 [.badges] -[.badge-key]##JVM since##[.badge-supported]##1.1.0## [.badge-key]##Native##[.badge-unsupported]##unsupported## +[.badge-key]##JVM since##[.badge-supported]##1.1.0## [.badge-key]##Native since##[.badge-supported]##1.8.0## Manage Droplets and resources within the DigitalOcean cloud. @@ -31,3 +31,9 @@ Please refer to the above link for usage and configuration details. ---- Check the xref:user-guide/index.adoc[User guide] for more information about writing Camel Quarkus applications. + +== SSL in native mode + +This extension auto-enables SSL support in native mode. Hence you do not need to add +`quarkus.ssl.native=true` to your `application.properties` yourself. See also +https://quarkus.io/guides/native-and-ssl[Quarkus SSL guide]. diff --git a/docs/modules/ROOT/partials/reference/components/digitalocean.adoc b/docs/modules/ROOT/partials/reference/components/digitalocean.adoc index 1e9fde7..705f406 100644 --- a/docs/modules/ROOT/partials/reference/components/digitalocean.adoc +++ b/docs/modules/ROOT/partials/reference/components/digitalocean.adoc @@ -2,11 +2,11 @@ // This file was generated by camel-quarkus-maven-plugin:update-extension-doc-page :cq-artifact-id: camel-quarkus-digitalocean :cq-artifact-id-base: digitalocean -:cq-native-supported: false -:cq-status: Preview +:cq-native-supported: true +:cq-status: Stable :cq-deprecated: false :cq-jvm-since: 1.1.0 -:cq-native-since: n/a +:cq-native-since: 1.8.0 :cq-camel-part-name: digitalocean :cq-camel-part-title: DigitalOcean :cq-camel-part-description: Manage Droplets and resources within the DigitalOcean cloud. diff --git a/extensions-jvm/digitalocean/deployment/src/main/java/org/apache/camel/quarkus/component/digitalocean/deployment/DigitaloceanProcessor.java b/extensions-jvm/digitalocean/deployment/src/main/java/org/apache/camel/quarkus/component/digitalocean/deployment/DigitaloceanProcessor.java deleted file mode 100644 index f2072ca..0000000 --- a/extensions-jvm/digitalocean/deployment/src/main/java/org/apache/camel/quarkus/component/digitalocean/deployment/DigitaloceanProcessor.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.camel.quarkus.component.digitalocean.deployment; - -import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.annotations.ExecutionTime; -import io.quarkus.deployment.annotations.Record; -import io.quarkus.deployment.builditem.FeatureBuildItem; -import io.quarkus.deployment.pkg.steps.NativeBuild; -import org.apache.camel.quarkus.core.JvmOnlyRecorder; -import org.jboss.logging.Logger; - -class DigitaloceanProcessor { - - private static final Logger LOG = Logger.getLogger(DigitaloceanProcessor.class); - private static final String FEATURE = "camel-digitalocean"; - - @BuildStep - FeatureBuildItem feature() { - return new FeatureBuildItem(FEATURE); - } - - /** - * Remove this once this extension starts supporting the native mode. - */ - @BuildStep(onlyIf = NativeBuild.class) - @Record(value = ExecutionTime.RUNTIME_INIT) - void warnJvmInNative(JvmOnlyRecorder recorder) { - JvmOnlyRecorder.warnJvmInNative(LOG, FEATURE); // warn at build time - recorder.warnJvmInNative(FEATURE); // warn at runtime - } -} diff --git a/extensions-jvm/digitalocean/integration-test/src/main/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanResource.java b/extensions-jvm/digitalocean/integration-test/src/main/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanResource.java deleted file mode 100644 index d8c6886..0000000 --- a/extensions-jvm/digitalocean/integration-test/src/main/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanResource.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.camel.quarkus.component.digitalocean.it; - -import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.apache.camel.CamelContext; -import org.jboss.logging.Logger; - -@Path("/digitalocean") -@ApplicationScoped -public class DigitaloceanResource { - - private static final Logger LOG = Logger.getLogger(DigitaloceanResource.class); - - private static final String COMPONENT_DIGITALOCEAN = "digitalocean"; - @Inject - CamelContext context; - - @Path("/load/component/digitalocean") - @GET - @Produces(MediaType.TEXT_PLAIN) - public Response loadComponentDigitalocean() throws Exception { - /* This is an autogenerated test */ - if (context.getComponent(COMPONENT_DIGITALOCEAN) != null) { - return Response.ok().build(); - } - LOG.warnf("Could not load [%s] from the Camel context", COMPONENT_DIGITALOCEAN); - return Response.status(500, COMPONENT_DIGITALOCEAN + " could not be loaded from the Camel context").build(); - } -} diff --git a/extensions-jvm/pom.xml b/extensions-jvm/pom.xml index f334f5e..12f524f 100644 --- a/extensions-jvm/pom.xml +++ b/extensions-jvm/pom.xml @@ -53,7 +53,6 @@ <module>cometd</module> <module>corda</module> <module>couchbase</module> - <module>digitalocean</module> <module>djl</module> <module>dns</module> <module>drill</module> diff --git a/extensions-jvm/digitalocean/deployment/pom.xml b/extensions/digitalocean/deployment/pom.xml similarity index 80% rename from extensions-jvm/digitalocean/deployment/pom.xml rename to extensions/digitalocean/deployment/pom.xml index 0670108..2b55dd6 100644 --- a/extensions-jvm/digitalocean/deployment/pom.xml +++ b/extensions/digitalocean/deployment/pom.xml @@ -37,6 +37,20 @@ <dependency> <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-digitalocean</artifactId> + <exclusions> + <exclusion> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpclient</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-support-commons-logging-deployment</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-support-httpclient-deployment</artifactId> </dependency> </dependencies> diff --git a/extensions/digitalocean/deployment/src/main/java/org/apache/camel/quarkus/component/digitalocean/deployment/DigitaloceanProcessor.java b/extensions/digitalocean/deployment/src/main/java/org/apache/camel/quarkus/component/digitalocean/deployment/DigitaloceanProcessor.java new file mode 100644 index 0000000..0c07ac8 --- /dev/null +++ b/extensions/digitalocean/deployment/src/main/java/org/apache/camel/quarkus/component/digitalocean/deployment/DigitaloceanProcessor.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.quarkus.component.digitalocean.deployment; + +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; +import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.IndexDependencyBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import org.jboss.jandex.IndexView; + +class DigitaloceanProcessor { + + private static final String FEATURE = "camel-digitalocean"; + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem(FEATURE); + } + + @BuildStep + ExtensionSslNativeSupportBuildItem activateSslNativeSupport() { + return new ExtensionSslNativeSupportBuildItem(FEATURE); + } + + @BuildStep + ReflectiveClassBuildItem registerForReflection(CombinedIndexBuildItem combinedIndex) { + IndexView index = combinedIndex.getIndex(); + // register Digitalocean model classes for JSON serialize/deserialize + String[] dtos = index.getKnownClasses().stream().map(ci -> ci.name().toString()) + .filter(n -> n.startsWith("com.myjeeva.digitalocean.pojo") + || n.startsWith("com.myjeeva.digitalocean.common")) + .sorted() + .toArray(String[]::new); + + return new ReflectiveClassBuildItem(true, true, dtos); + } + + @BuildStep + void addDependencies(BuildProducer<IndexDependencyBuildItem> indexDependency) { + indexDependency.produce(new IndexDependencyBuildItem("com.myjeeva.digitalocean", "digitalocean-api-client")); + } + +} diff --git a/extensions-jvm/digitalocean/pom.xml b/extensions/digitalocean/pom.xml similarity index 97% rename from extensions-jvm/digitalocean/pom.xml rename to extensions/digitalocean/pom.xml index 2b033c7..e43cbc3 100644 --- a/extensions-jvm/digitalocean/pom.xml +++ b/extensions/digitalocean/pom.xml @@ -33,6 +33,5 @@ <modules> <module>deployment</module> <module>runtime</module> - <module>integration-test</module> </modules> </project> diff --git a/extensions-jvm/digitalocean/runtime/pom.xml b/extensions/digitalocean/runtime/pom.xml similarity index 91% rename from extensions-jvm/digitalocean/runtime/pom.xml rename to extensions/digitalocean/runtime/pom.xml index 068427d..857fc61 100644 --- a/extensions-jvm/digitalocean/runtime/pom.xml +++ b/extensions/digitalocean/runtime/pom.xml @@ -32,6 +32,7 @@ <properties> <camel.quarkus.jvmSince>1.1.0</camel.quarkus.jvmSince> + <camel.quarkus.nativeSince>1.8.0</camel.quarkus.nativeSince> </properties> <dependencyManagement> @@ -55,6 +56,14 @@ <groupId>org.apache.camel</groupId> <artifactId>camel-digitalocean</artifactId> </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-support-commons-logging</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-support-httpclient</artifactId> + </dependency> </dependencies> <build> diff --git a/extensions-jvm/digitalocean/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/digitalocean/runtime/src/main/resources/META-INF/quarkus-extension.yaml similarity index 97% rename from extensions-jvm/digitalocean/runtime/src/main/resources/META-INF/quarkus-extension.yaml rename to extensions/digitalocean/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 00fdd9c..4436c9c 100644 --- a/extensions-jvm/digitalocean/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/digitalocean/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -24,9 +24,8 @@ name: "Camel DigitalOcean" description: "Manage Droplets and resources within the DigitalOcean cloud" metadata: - unlisted: true guide: "https://camel.apache.org/camel-quarkus/latest/reference/extensions/digitalocean.html" categories: - "integration" status: - - "preview" + - "stable" diff --git a/extensions/pom.xml b/extensions/pom.xml index 72e26e1..99d700c 100644 --- a/extensions/pom.xml +++ b/extensions/pom.xml @@ -89,6 +89,7 @@ <module>debezium-mysql</module> <module>debezium-postgres</module> <module>debezium-sqlserver</module> + <module>digitalocean</module> <module>direct</module> <module>disruptor</module> <module>dozer</module> diff --git a/integration-tests-support/wiremock/src/main/java/org/apache/camel/quarkus/test/wiremock/WireMockTestResourceLifecycleManager.java b/integration-tests-support/wiremock/src/main/java/org/apache/camel/quarkus/test/wiremock/WireMockTestResourceLifecycleManager.java index d3c6066..951d45f 100644 --- a/integration-tests-support/wiremock/src/main/java/org/apache/camel/quarkus/test/wiremock/WireMockTestResourceLifecycleManager.java +++ b/integration-tests-support/wiremock/src/main/java/org/apache/camel/quarkus/test/wiremock/WireMockTestResourceLifecycleManager.java @@ -68,9 +68,11 @@ public abstract class WireMockTestResourceLifecycleManager implements QuarkusTes } } - String wireMockUrl = "http://localhost:" + server.port(); + String wireMockUrl = "http://localhost:" + this.server.port(); LOG.infof("WireMock started on %s", wireMockUrl); properties.put("wiremock.url", wireMockUrl); + String httpsUrl = "https://localhost:" + this.server.httpsPort(); + properties.put("wiremock.url.ssl", httpsUrl); } return properties; @@ -197,7 +199,7 @@ public abstract class WireMockTestResourceLifecycleManager implements QuarkusTes protected abstract boolean isMockingEnabled(); /** - * Customizes the {@link WiremockConfiguration} that will be used to create the next {@Link WireMockServer}. + * Customizes the {@link WireMockConfiguration} that will be used to create the next {@Link WireMockServer}. */ protected void customizeWiremockConfiguration(WireMockConfiguration config) { } @@ -217,6 +219,9 @@ public abstract class WireMockTestResourceLifecycleManager implements QuarkusTes // Read mapping resources from the classpath in playback mode configuration.fileSource(new CamelQuarkusFileSource()); } + + // add an SSL port + configuration.dynamicHttpsPort(); return new WireMockServer(configuration); } diff --git a/integration-tests/digitalocean/README.adoc b/integration-tests/digitalocean/README.adoc new file mode 100644 index 0000000..bad96cf --- /dev/null +++ b/integration-tests/digitalocean/README.adoc @@ -0,0 +1,13 @@ +== Camel Quarkus Digitalocean Integration Tests + +By default, the Digitalocean integration tests use WireMock to stub the API interactions. + +To run `camel-quarkus-digitalocean` integration tests using Digitalocean API interactions, you will need a Digitalocean Token. Create a Digitalocean https://www.digitalocean.com/docs/apis-clis/api/create-personal-access-token/[Access Token]. + +Then set the following environment variable: + +[source,shell] +---- +DIGITALOCEAN_AUTH_TOKEN=your-api-token +---- + diff --git a/integration-tests/digitalocean/pom.xml b/integration-tests/digitalocean/pom.xml new file mode 100644 index 0000000..0e7c521 --- /dev/null +++ b/integration-tests/digitalocean/pom.xml @@ -0,0 +1,158 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-integration-tests</artifactId> + <version>1.8.0-SNAPSHOT</version> + </parent> + + <artifactId>camel-quarkus-integration-test-digitalocean</artifactId> + <name>Camel Quarkus :: Integration Tests :: DigitalOcean</name> + <description>Integration tests for Camel Quarkus DigitalOcean extension</description> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-bom-test</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-digitalocean</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-resteasy-jackson</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-mock</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-direct</artifactId> + </dependency> + + <!-- test dependencies --> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-junit5</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>io.rest-assured</groupId> + <artifactId>rest-assured</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.awaitility</groupId> + <artifactId>awaitility</artifactId> + <scope>test</scope> + </dependency> + + <!-- dependencies needed to mock Digitalocean API --> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-integration-wiremock-support</artifactId> + </dependency> + + <!-- The following dependencies guarantee that this module is built after them. You can update them by running `mvn process-resources -Pformat -N` from the source tree root directory --> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-digitalocean-deployment</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-direct-deployment</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-mock-deployment</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + </dependencies> + + <profiles> + <profile> + <id>native</id> + <activation> + <property> + <name>native</name> + </property> + </activation> + <properties> + <quarkus.package.type>native</quarkus.package.type> + </properties> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-failsafe-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>integration-test</goal> + <goal>verify</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> + +</project> diff --git a/extensions-jvm/digitalocean/integration-test/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTest.java b/integration-tests/digitalocean/src/main/java/org/apache/camel/quarkus/component/digitalocean/it/DigitalOceanClientMock.java similarity index 70% copy from extensions-jvm/digitalocean/integration-test/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTest.java copy to integration-tests/digitalocean/src/main/java/org/apache/camel/quarkus/component/digitalocean/it/DigitalOceanClientMock.java index 4c34b9d..462799f 100644 --- a/extensions-jvm/digitalocean/integration-test/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTest.java +++ b/integration-tests/digitalocean/src/main/java/org/apache/camel/quarkus/component/digitalocean/it/DigitalOceanClientMock.java @@ -16,19 +16,15 @@ */ package org.apache.camel.quarkus.component.digitalocean.it; -import io.quarkus.test.junit.QuarkusTest; -import io.restassured.RestAssured; -import org.junit.jupiter.api.Test; +import com.github.tomakehurst.wiremock.http.HttpClientFactory; +import com.myjeeva.digitalocean.impl.DigitalOceanClient; -@QuarkusTest -class DigitaloceanTest { +public class DigitalOceanClientMock extends DigitalOceanClient { - @Test - public void loadComponentDigitalocean() { - /* A simple autogenerated test */ - RestAssured.get("/digitalocean/load/component/digitalocean") - .then() - .statusCode(200); + public DigitalOceanClientMock(String authToken, String apiHost) { + super(authToken); + this.apiHost = apiHost; + this.httpClient = HttpClientFactory.createClient(); } } diff --git a/integration-tests/digitalocean/src/main/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanResource.java b/integration-tests/digitalocean/src/main/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanResource.java new file mode 100644 index 0000000..bb8f69b --- /dev/null +++ b/integration-tests/digitalocean/src/main/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanResource.java @@ -0,0 +1,237 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.quarkus.component.digitalocean.it; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.Response; + +import com.myjeeva.digitalocean.pojo.Action; +import com.myjeeva.digitalocean.pojo.Backup; +import com.myjeeva.digitalocean.pojo.Droplet; +import com.myjeeva.digitalocean.pojo.Kernel; +import com.myjeeva.digitalocean.pojo.Snapshot; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.component.digitalocean.constants.DigitalOceanHeaders; +import org.apache.camel.component.digitalocean.constants.DigitalOceanOperations; +import org.jboss.logging.Logger; + +@Path("digitalocean/droplet") +@ApplicationScoped +public class DigitaloceanResource { + + private static final Logger LOG = Logger.getLogger(DigitaloceanResource.class); + private static final String REGION_FRANKFURT = "fra1"; + private static final String DROPLET_SIZE_1_GB = "s-1vcpu-1gb"; + private static final String DROPLET_IMAGE = "ubuntu-20-04-x64"; + + @Inject + ProducerTemplate producerTemplate; + + @PUT + @Path("{name}") + public int createDroplet(@PathParam("name") String name) { + LOG.infof("creating a droplet with name %s", name); + Map<String, Object> headers = getCreateHeaders(); + headers.put(DigitalOceanHeaders.NAME, name); + Droplet droplet = producerTemplate.requestBodyAndHeaders("direct:droplet", null, headers, Droplet.class); + return droplet.getId(); + } + + @PUT + public List<Integer> createDroplet(List<String> names) { + LOG.infof("creating a droplet with names %s", names); + Map<String, Object> headers = getCreateHeaders(); + headers.put(DigitalOceanHeaders.NAMES, names); + List<Droplet> droplets = producerTemplate.requestBodyAndHeaders("direct:droplet", null, headers, List.class); + return droplets.stream().map(droplet -> droplet.getId()).collect(Collectors.toList()); + } + + private Map<String, Object> getCreateHeaders() { + Map<String, Object> headers = new HashMap<>(); + headers.put(DigitalOceanHeaders.OPERATION, DigitalOceanOperations.create); + headers.put(DigitalOceanHeaders.REGION, REGION_FRANKFURT); + headers.put(DigitalOceanHeaders.DROPLET_SIZE, DROPLET_SIZE_1_GB); + headers.put(DigitalOceanHeaders.DROPLET_IMAGE, DROPLET_IMAGE); + Collection<String> tags = new ArrayList<>(); + tags.add("tag1"); + tags.add("tag2"); + headers.put(DigitalOceanHeaders.DROPLET_TAGS, tags); + return headers; + } + + @DELETE + @Path("{id}") + public Response deleteDroplet(@PathParam("id") int id) { + LOG.infof("deleting a droplet with id %s", id); + Map<String, Object> headers = new HashMap<>(); + headers.put(DigitalOceanHeaders.OPERATION, DigitalOceanOperations.delete); + headers.put(DigitalOceanHeaders.ID, id); + producerTemplate.sendBodyAndHeaders("direct:droplet", null, headers); + return Response.accepted().build(); + } + + @GET + @Path("{id}") + public Droplet getDroplet(@PathParam("id") int id) { + LOG.infof("getting droplet with id %s", id); + Map<String, Object> headers = new HashMap<>(); + headers.put(DigitalOceanHeaders.OPERATION, DigitalOceanOperations.get); + headers.put(DigitalOceanHeaders.ID, id); + Droplet droplet = producerTemplate.requestBodyAndHeaders("direct:droplet", null, headers, Droplet.class); + return droplet; + } + + @GET + @Path("actions/{id}") + public List<Action> getActions(@PathParam("id") int id) { + LOG.infof("getting actions's droplet with id %s", id); + Map<String, Object> headers = new HashMap<>(); + headers.put(DigitalOceanHeaders.OPERATION, DigitalOceanOperations.listActions); + headers.put(DigitalOceanHeaders.ID, id); + List<Action> actions = producerTemplate.requestBodyAndHeaders("direct:droplet", null, headers, List.class); + return actions; + } + + @GET + @Path("kernels/{id}") + public List<Kernel> getKernels(@PathParam("id") int id) { + LOG.infof("getting kernels's droplet with id %s", id); + Map<String, Object> headers = new HashMap<>(); + headers.put(DigitalOceanHeaders.OPERATION, DigitalOceanOperations.listKernels); + headers.put(DigitalOceanHeaders.ID, id); + List<Kernel> kernels = producerTemplate.requestBodyAndHeaders("direct:droplet", null, headers, List.class); + return kernels; + } + + @POST + @Path("snapshot/{id}") + public Action snapshotDroplet(@PathParam("id") int id, String snapshotName) { + LOG.infof("snapshot droplet %s with name %s", id, snapshotName); + Map<String, Object> headers = new HashMap<>(); + headers.put(DigitalOceanHeaders.OPERATION, DigitalOceanOperations.takeSnapshot); + headers.put(DigitalOceanHeaders.ID, id); + headers.put(DigitalOceanHeaders.NAME, snapshotName); + return producerTemplate.requestBodyAndHeaders("direct:droplet", null, headers, Action.class); + } + + @GET + @Path("snapshots/{id}") + public List<Snapshot> getSnapshots(@PathParam("id") int id) { + LOG.infof("getting snapshot's droplet with id %s", id); + Map<String, Object> headers = new HashMap<>(); + headers.put(DigitalOceanHeaders.OPERATION, DigitalOceanOperations.listSnapshots); + headers.put(DigitalOceanHeaders.ID, id); + List<Snapshot> snapshots = producerTemplate.requestBodyAndHeaders("direct:droplet", null, headers, List.class); + return snapshots; + } + + @GET + @Path("backups/enable/{id}") + public Action enableBackups(@PathParam("id") int id) { + LOG.infof("Enable backups for droplet %s", id); + return doAction(id, DigitalOceanOperations.enableBackups); + } + + @GET + @Path("backups/disable/{id}") + public Action disableBackups(@PathParam("id") int id) { + LOG.infof("disable backups for droplet %s", id); + return doAction(id, DigitalOceanOperations.disableBackups); + } + + @GET + @Path("on/{id}") + public Action turnOn(@PathParam("id") int id) { + LOG.infof("Turn on droplet %s", id); + return doAction(id, DigitalOceanOperations.powerOn); + } + + @GET + @Path("off/{id}") + public Action turnOff(@PathParam("id") int id) { + LOG.infof("Turn off droplet %s", id); + return doAction(id, DigitalOceanOperations.powerOff); + } + + @GET + @Path("reboot/{id}") + public Action rebootDroplet(@PathParam("id") int id) { + LOG.infof("Reboot droplet %s", id); + return doAction(id, DigitalOceanOperations.reboot); + } + + @GET + @Path("ipv6/{id}") + public Action enableIpv6(@PathParam("id") int id) { + LOG.infof("Enable Ipv6 for droplet %s", id); + return doAction(id, DigitalOceanOperations.enableIpv6); + } + + private Action doAction(int id, DigitalOceanOperations operation) { + Map<String, Object> headers = new HashMap<>(); + headers.put(DigitalOceanHeaders.OPERATION, operation); + headers.put(DigitalOceanHeaders.ID, id); + return producerTemplate.requestBodyAndHeaders("direct:droplet", null, headers, Action.class); + } + + @GET + @Path("backups/{id}") + public List<Backup> getBackups(@PathParam("id") int id) { + LOG.infof("getting backups's droplet with id %s", id); + Map<String, Object> headers = new HashMap<>(); + headers.put(DigitalOceanHeaders.OPERATION, DigitalOceanOperations.listBackups); + headers.put(DigitalOceanHeaders.ID, id); + List<Backup> backups = producerTemplate.requestBodyAndHeaders("direct:droplet", null, headers, List.class); + return backups; + } + + @GET + @Path("neighbors/{id}") + public List<Droplet> getNeighbors(@PathParam("id") int id) { + LOG.infof("getting neighbors's droplet with id %s", id); + Map<String, Object> headers = new HashMap<>(); + headers.put(DigitalOceanHeaders.OPERATION, DigitalOceanOperations.listNeighbors); + headers.put(DigitalOceanHeaders.ID, id); + List<Droplet> neighbors = producerTemplate.requestBodyAndHeaders("direct:droplet", null, headers, List.class); + return neighbors; + } + + @GET + @Path("neighbors") + public List<Droplet> getAllNeighbors() { + LOG.infof("getting all neighbors"); + Map<String, Object> headers = new HashMap<>(); + headers.put(DigitalOceanHeaders.OPERATION, DigitalOceanOperations.listAllNeighbors); + List<Droplet> neighbors = producerTemplate.requestBodyAndHeaders("direct:droplet", null, headers, List.class); + return neighbors; + } + +} diff --git a/integration-tests/digitalocean/src/main/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanRoute.java b/integration-tests/digitalocean/src/main/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanRoute.java new file mode 100644 index 0000000..9724f0d --- /dev/null +++ b/integration-tests/digitalocean/src/main/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanRoute.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.quarkus.component.digitalocean.it; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Named; +import javax.ws.rs.Produces; + +import com.myjeeva.digitalocean.impl.DigitalOceanClient; +import io.quarkus.arc.Unremovable; +import org.apache.camel.builder.RouteBuilder; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +@ApplicationScoped +public class DigitaloceanRoute extends RouteBuilder { + @ConfigProperty(name = "DIGITALOCEAN_AUTH_TOKEN", defaultValue = "NONE") + String oAuthToken; + + /** + * We need to implement some conditional configuration of the {@link DigitalOceanClient} thus we create it + * programmatically + * + * @return a configured {@link DigitalOceanClient} + */ + @Produces + @ApplicationScoped + @Unremovable + @Named("digitalOceanClient") + DigitalOceanClient intiDigitalOceanClient(MockApiService mockApiService) { + final String wireMockUrl = System.getProperty("wiremock.url.ssl"); + if (wireMockUrl != null) { + DigitalOceanClient digitalOceanClient = mockApiService.createDigitalOceanClient(wireMockUrl, oAuthToken); + digitalOceanClient.setAuthToken(oAuthToken); + digitalOceanClient.setApiVersion("V2"); + return digitalOceanClient; + } + return new DigitalOceanClient(oAuthToken); + } + + @Override + public void configure() throws Exception { + from("direct:droplet") + .to(String.format("digitalocean:droplets?oAuthToken=%s&digitalOceanClient=#digitalOceanClient", oAuthToken)); + + } +} diff --git a/extensions-jvm/digitalocean/integration-test/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTest.java b/integration-tests/digitalocean/src/main/java/org/apache/camel/quarkus/component/digitalocean/it/MockApiService.java similarity index 62% copy from extensions-jvm/digitalocean/integration-test/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTest.java copy to integration-tests/digitalocean/src/main/java/org/apache/camel/quarkus/component/digitalocean/it/MockApiService.java index 4c34b9d..1aee355 100644 --- a/extensions-jvm/digitalocean/integration-test/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTest.java +++ b/integration-tests/digitalocean/src/main/java/org/apache/camel/quarkus/component/digitalocean/it/MockApiService.java @@ -16,19 +16,19 @@ */ package org.apache.camel.quarkus.component.digitalocean.it; -import io.quarkus.test.junit.QuarkusTest; -import io.restassured.RestAssured; -import org.junit.jupiter.api.Test; +import javax.enterprise.context.ApplicationScoped; -@QuarkusTest -class DigitaloceanTest { +import com.myjeeva.digitalocean.impl.DigitalOceanClient; - @Test - public void loadComponentDigitalocean() { - /* A simple autogenerated test */ - RestAssured.get("/digitalocean/load/component/digitalocean") - .then() - .statusCode(200); +@ApplicationScoped +public class MockApiService { + + public DigitalOceanClient createDigitalOceanClient(String wireMockUrl, String oAuthToken) { + DigitalOceanClientMock digitalOceanClient = new DigitalOceanClientMock(oAuthToken, getFormattedURI(wireMockUrl)); + return digitalOceanClient; } + String getFormattedURI(String wireMockUrl) { + return wireMockUrl.replaceAll("https://", ""); + } } diff --git a/extensions-jvm/digitalocean/integration-test/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTest.java b/integration-tests/digitalocean/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanIT.java similarity index 69% copy from extensions-jvm/digitalocean/integration-test/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTest.java copy to integration-tests/digitalocean/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanIT.java index 4c34b9d..c23af07 100644 --- a/extensions-jvm/digitalocean/integration-test/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTest.java +++ b/integration-tests/digitalocean/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanIT.java @@ -16,19 +16,9 @@ */ package org.apache.camel.quarkus.component.digitalocean.it; -import io.quarkus.test.junit.QuarkusTest; -import io.restassured.RestAssured; -import org.junit.jupiter.api.Test; +import io.quarkus.test.junit.NativeImageTest; -@QuarkusTest -class DigitaloceanTest { - - @Test - public void loadComponentDigitalocean() { - /* A simple autogenerated test */ - RestAssured.get("/digitalocean/load/component/digitalocean") - .then() - .statusCode(200); - } +@NativeImageTest +class DigitaloceanIT extends DigitaloceanTest { } diff --git a/integration-tests/digitalocean/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTest.java b/integration-tests/digitalocean/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTest.java new file mode 100644 index 0000000..a956b62 --- /dev/null +++ b/integration-tests/digitalocean/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTest.java @@ -0,0 +1,231 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.quarkus.component.digitalocean.it; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import com.github.tomakehurst.wiremock.WireMockServer; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.apache.camel.quarkus.test.wiremock.MockServer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; + +import static io.restassured.RestAssured.given; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.hasKey; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@QuarkusTest +@QuarkusTestResource(DigitaloceanTestResource.class) +class DigitaloceanTest { + + @MockServer + WireMockServer server; + + @Test + @DisabledIfEnvironmentVariable(named = "DIGITALOCEAN_AUTH_TOKEN", matches = ".+") + public void testIfMock() { + testDroplets(5, TimeUnit.SECONDS, false); + } + + @Test + @EnabledIfEnvironmentVariable(named = "DIGITALOCEAN_AUTH_TOKEN", matches = ".+") + public void testIfOAuth() { + testDroplets(10, TimeUnit.MINUTES, true); + } + + public void testDroplets(long timeout, TimeUnit timeOutUnit, boolean waitPowerOff) { + // insert multiple droplets + List<String> names = Arrays.asList("droplet1"); + List<Integer> otherIds = RestAssured.given().contentType(ContentType.JSON).body(names).put("/digitalocean/droplet") + .then().extract().body().as(List.class); + assertNotNull(otherIds); + assertEquals(1, otherIds.size()); + Integer dropletId1 = otherIds.get(0); + + // get the droplet by dropletId1 until its status is active and ready + // it takes time only if using a oAuthToken from Digitalocean + waitActiveDroplet(dropletId1, timeout, timeOutUnit); + + // action : enable backups + given() + .contentType(ContentType.JSON) + .get("/digitalocean/droplet/backups/enable/" + dropletId1) + .then() + .body("resourceId", equalTo(dropletId1)) + .body("type", equalTo("ENABLE_BACKUPS")); + + // action : power off, before taking a snapshot + given() + .contentType(ContentType.JSON) + .get("/digitalocean/droplet/off/" + dropletId1) + .then() + .body("resourceId", equalTo(dropletId1)); + + // take a snapshot + given() + .contentType(ContentType.JSON) + .body("snapshot1") + .post("/digitalocean/droplet/snapshot/" + dropletId1) + .then() + .body("resourceId", equalTo(dropletId1)); + + // action : get and wait for the snapshot + waitForSnapshot(dropletId1, timeout, timeOutUnit); + + // action : disable backups + given() + .when() + .get("/digitalocean/droplet/backups/disable/" + dropletId1) + .then() + .body("resourceId", equalTo(dropletId1)); + + // wait for Droplet to be active + waitActiveDroplet(dropletId1, timeout, timeOutUnit); + + // action : power on + given() + .contentType(ContentType.JSON) + .get("/digitalocean/droplet/on/" + dropletId1) + .then() + .body("resourceId", equalTo(dropletId1)); + + // Reboot droplet + given() + .contentType(ContentType.JSON) + .get("/digitalocean/droplet/reboot/" + dropletId1) + .then() + .body("resourceId", equalTo(dropletId1)); + + // wait for Droplet to be active + waitActiveDroplet(dropletId1, timeout, timeOutUnit); + + // enable Ipv6 + given() + .contentType(ContentType.JSON) + .get("/digitalocean/droplet/ipv6/" + dropletId1) + .then() + .body("resourceId", equalTo(dropletId1)); + + // getting the droplet actions + List<Map> actions = RestAssured.given().contentType(ContentType.JSON).body(names) + .get("/digitalocean/droplet/actions/" + dropletId1) + .then().extract().body().as(List.class); + assertActions(actions); + + // delete the droplet + given() + .when() + .delete("/digitalocean/droplet/" + dropletId1) + .then() + .statusCode(202); + } + + /** + * Gets the snapshots and waits until the snapshot is created in Digitalocean. It may take 2 to 3 minutes. For tests + * with Wiremock, no need to wait + * + * @param dropletId1 + */ + private void waitForSnapshot(Integer dropletId1, long timeout, TimeUnit timeOutUnit) { + await().atMost(timeout, timeOutUnit).until(() -> { + String path = "/digitalocean/droplet/snapshots/" + dropletId1; + List<Map> result = given().when().get(path).then().extract().as(List.class); + // Look for the snapshot + Optional optional = result.stream() + .filter(s -> "snapshot1".equals(s.get("name"))) + .findAny(); + return optional.isPresent(); + }); + } + + /** + * Gets the droplet and waits until the droplet is Active in Digitalocean. It may take 2 to 3 minutes. For tests with + * Wiremock, the response is always Active + * + * @param dropletId1 + */ + private void waitActiveDroplet(Integer dropletId1, long timeout, TimeUnit timeOutUnit) { + await().atMost(timeout, timeOutUnit).until(() -> { + String path = "/digitalocean/droplet/" + dropletId1; + Map droplet = given() + .contentType(ContentType.JSON).get(path).then().extract().as(Map.class); + return droplet != null && dropletId1.equals(droplet.get("id")) && "ACTIVE".equals(droplet.get("status")); + }); + } + + /** + * Assert all the actions + * + * @param actions + */ + private void assertActions(List<Map> actions) { + // verify there are actions + assertNotNull(actions); + // verify there are at least the 7 created actions in the test + assertTrue(actions.size() >= 7); + List<String> types = Arrays.asList("ENABLE_BACKUPS", "DISABLE_BACKUPS", "SNAPSHOT", "POWER_ON", "POWER_OFF", "REBOOT", + "ENABLE_IPV6"); + types.forEach(type -> assertAction(actions, type)); + } + + /** + * assert a single action + * + * @param actions + * @param actionType + */ + private void assertAction(List<Map> actions, String actionType) { + Optional<Map> optional = actions.stream() + .filter(a -> actionType.equals(a.get("type"))) + .findAny(); + assertTrue(optional.isPresent()); + } + + @Test + @DisabledIfEnvironmentVariable(named = "DIGITALOCEAN_AUTH_TOKEN", matches = ".+") + void testGetKernels() { + // only for mock back-end server + given() + .when() + .get("/digitalocean/droplet/kernels/3164494") + .then() + .body("[0]", hasKey("id")); + } + + @Test + @DisabledIfEnvironmentVariable(named = "DIGITALOCEAN_AUTH_TOKEN", matches = ".+") + void testNeighbors() { + // only for mock back-end server + given() + .when() + .get("/digitalocean/droplet/neighbors/3164494") + .then() + .body("[0].id", equalTo(3164495)); + } +} diff --git a/extensions-jvm/digitalocean/integration-test/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTest.java b/integration-tests/digitalocean/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTestResource.java similarity index 61% rename from extensions-jvm/digitalocean/integration-test/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTest.java rename to integration-tests/digitalocean/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTestResource.java index 4c34b9d..2beb182 100644 --- a/extensions-jvm/digitalocean/integration-test/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTest.java +++ b/integration-tests/digitalocean/src/test/java/org/apache/camel/quarkus/component/digitalocean/it/DigitaloceanTestResource.java @@ -16,19 +16,19 @@ */ package org.apache.camel.quarkus.component.digitalocean.it; -import io.quarkus.test.junit.QuarkusTest; -import io.restassured.RestAssured; -import org.junit.jupiter.api.Test; +import org.apache.camel.quarkus.test.wiremock.WireMockTestResourceLifecycleManager; -@QuarkusTest -class DigitaloceanTest { +public class DigitaloceanTestResource extends WireMockTestResourceLifecycleManager { + private static final String DIGITALOCEAN_BASE_URL = "api.digitalocean.com"; + private static final String DIGITALOCEAN_AUTH_TOKEN = "DIGITALOCEAN_AUTH_TOKEN"; - @Test - public void loadComponentDigitalocean() { - /* A simple autogenerated test */ - RestAssured.get("/digitalocean/load/component/digitalocean") - .then() - .statusCode(200); + @Override + protected String getRecordTargetBaseUrl() { + return DIGITALOCEAN_BASE_URL; } + @Override + protected boolean isMockingEnabled() { + return !envVarsPresent(DIGITALOCEAN_AUTH_TOKEN); + } } diff --git a/integration-tests/digitalocean/src/test/resources/mappings/actionsDroplet.json b/integration-tests/digitalocean/src/test/resources/mappings/actionsDroplet.json new file mode 100644 index 0000000..d034a5c --- /dev/null +++ b/integration-tests/digitalocean/src/test/resources/mappings/actionsDroplet.json @@ -0,0 +1,28 @@ +{ + "id": "09aa79df-8a19-41df-8655-e414d390d6ed", + "name": "digitalocean_api_json", + "request": { + "url": "/V2/droplets/3164494/actions?page=1&per_page=25", + "method": "GET" + }, + "response": { + "status": 200, + "body": "{\"actions\":[{\"id\":36805187,\"status\":\"completed\",\"type\":\"snapshot\",\"started_at\":\"2014-11-14T16:37:34Z\",\"completed_at\":\"2014-11-14T16:39:32Z\",\"resource_id\":3164494,\"resource_type\":\"droplet\",\"region\":{\"name\":\"New York 3\",\"slug\":\"nyc3\",\"sizes\":[\"s-1vcpu-3gb\",\"m-1vcpu-8gb\",\"s-3vcpu-1gb\",\"s-1vcpu-2gb\",\"s-2vcpu-2gb\",\"s-2vcpu-4gb\",\"s-4vcpu-8gb\",\"s-6vcpu-16gb\",\"s-8vcpu-32gb\",\"s-12vcpu-48gb\",\"s-16vcpu-64gb\",\"s-20vcpu-96gb\", [...] + "headers": { + "Content-Type": "application/json; charset=UTF-8", + "Date": "Tue, 03 Nov 2020 09:39:11 GMT", + "Pragma": "no-cache", + "Expires": "Fri, 01 Jan 1990 00:00:00 GMT", + "Cache-Control": "no-cache, must-revalidate", + "Vary": "Accept-Language", + "Server": "mafe", + "X-XSS-Protection": "0", + "X-Frame-Options": "SAMEORIGIN", + "Server-Timing": "gfet4t7; dur=263", + "Alt-Svc": "h3-Q050=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-T050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"" + } + }, + "uuid": "09aa79df-8a19-41df-8655-e414d390d6ed", + "persistent": true, + "insertionIndex": 2 +} \ No newline at end of file diff --git a/integration-tests/digitalocean/src/test/resources/mappings/backupsDroplet.json b/integration-tests/digitalocean/src/test/resources/mappings/backupsDroplet.json new file mode 100644 index 0000000..ae8cb7b --- /dev/null +++ b/integration-tests/digitalocean/src/test/resources/mappings/backupsDroplet.json @@ -0,0 +1,28 @@ +{ + "id": "09aa79df-8a19-41df-8655-e414d390d6ed", + "name": "digitalocean_api_json", + "request": { + "url": "/V2/droplets/3164494/backups?page=1&per_page=25", + "method": "GET" + }, + "response": { + "status": 200, + "body" : "{\"backups\":[{\"id\":7622989,\"name\":\"example.com 2014-11-14\",\"distribution\":\"Ubuntu\",\"slug\":null,\"public\":false,\"regions\":[\"nyc3\"],\"created_at\":\"2014-11-14T16:07:38Z\",\"type\":\"snapshot\",\"min_disk_size\":20,\"size_gigabytes\":2.34}],\"meta\":{\"total\":1}}", + "headers": { + "Content-Type": "application/json; charset=UTF-8", + "Date": "Tue, 03 Nov 2020 09:39:11 GMT", + "Pragma": "no-cache", + "Expires": "Fri, 01 Jan 1990 00:00:00 GMT", + "Cache-Control": "no-cache, must-revalidate", + "Vary": "Accept-Language", + "Server": "mafe", + "X-XSS-Protection": "0", + "X-Frame-Options": "SAMEORIGIN", + "Server-Timing": "gfet4t7; dur=263", + "Alt-Svc": "h3-Q050=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-T050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"" + } + }, + "uuid": "09aa79df-8a19-41df-8655-e414d390d6ed", + "persistent": true, + "insertionIndex": 2 +} \ No newline at end of file diff --git a/integration-tests/digitalocean/src/test/resources/mappings/createMultipleDroplets.json b/integration-tests/digitalocean/src/test/resources/mappings/createMultipleDroplets.json new file mode 100644 index 0000000..3ae8ae1 --- /dev/null +++ b/integration-tests/digitalocean/src/test/resources/mappings/createMultipleDroplets.json @@ -0,0 +1,28 @@ +{ + "id": "09aa79df-8a19-41df-8655-e414d390d6eb", + "name": "digitalocean_api_json", + "request": { + "url": "/V2/droplets", + "method": "POST" + }, + "response": { + "status": 200, + "body" : "{\"droplets\":[{\"id\":3164494,\"name\":\"sub-01.example.com\",\"memory\":1024,\"vcpus\":1,\"disk\":25,\"locked\":false,\"status\":\"new\",\"kernel\":{\"id\":2233,\"name\":\"Ubuntu 14.04 x64 vmlinuz-3.13.0-37-generic\",\"version\":\"3.13.0-37-generic\"},\"created_at\":\"2014-11-14T16:36:31Z\",\"features\":[\"virtio\"],\"backup_ids\":[],\"snapshot_ids\":[],\"image\":{},\"volume_ids\":[],\"size\":{},\"size_slug\":\"s-1vcpu-1gb\",\"networks\":{},\"region\":{},\"tags\":[\"web\" [...] + "headers": { + "Content-Type": "application/json; charset=UTF-8", + "Date": "Tue, 03 Nov 2020 09:39:11 GMT", + "Pragma": "no-cache", + "Expires": "Fri, 01 Jan 1990 00:00:00 GMT", + "Cache-Control": "no-cache, must-revalidate", + "Vary": "Accept-Language", + "Server": "mafe", + "X-XSS-Protection": "0", + "X-Frame-Options": "SAMEORIGIN", + "Server-Timing": "gfet4t7; dur=263", + "Alt-Svc": "h3-Q050=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-T050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"" + } + }, + "uuid": "09aa79df-8a19-41df-8655-e414d390d6eb", + "persistent": true, + "insertionIndex": 2 +} \ No newline at end of file diff --git a/integration-tests/digitalocean/src/test/resources/mappings/deleteDroplet.json b/integration-tests/digitalocean/src/test/resources/mappings/deleteDroplet.json new file mode 100644 index 0000000..b2945a1 --- /dev/null +++ b/integration-tests/digitalocean/src/test/resources/mappings/deleteDroplet.json @@ -0,0 +1,28 @@ +{ + "id": "09aa79df-8a19-41df-8655-e414d390d6ea", + "name": "digitalocean_api_json", + "request": { + "url": "/V2/droplets/3164494", + "method": "DELETE" + }, + "response": { + "status": 204, + "body" : "{\"delete\": {\"request_status\": true, \"status_code\": 204}, \"rate_limit\": { \"limit\": 5000, \"remaining\": 4998, \"reset\": \"2021-03-23T12:13:10+01\"}}", + "headers": { + "Content-Type": "application/json; charset=UTF-8", + "Date": "Tue, 03 Nov 2020 09:39:11 GMT", + "Pragma": "no-cache", + "Expires": "Fri, 01 Jan 1990 00:00:00 GMT", + "Cache-Control": "no-cache, must-revalidate", + "Vary": "Accept-Language", + "Server": "mafe", + "X-XSS-Protection": "0", + "X-Frame-Options": "SAMEORIGIN", + "Server-Timing": "gfet4t7; dur=263", + "Alt-Svc": "h3-Q050=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-T050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"" + } + }, + "uuid": "09aa79df-8a19-41df-8655-e414d390d6ea", + "persistent": true, + "insertionIndex": 2 +} \ No newline at end of file diff --git a/integration-tests/digitalocean/src/test/resources/mappings/dropletAction.json b/integration-tests/digitalocean/src/test/resources/mappings/dropletAction.json new file mode 100644 index 0000000..6f48d61 --- /dev/null +++ b/integration-tests/digitalocean/src/test/resources/mappings/dropletAction.json @@ -0,0 +1,28 @@ +{ + "id": "09aa79df-8a19-41df-8655-e414d390d6ed", + "name": "digitalocean_api_json", + "request": { + "url": "/V2/droplets/3164494/actions", + "method": "POST" + }, + "response": { + "status": 200, + "body" : "{\"action\":{\"id\":36804745,\"status\":\"in-progress\",\"type\":\"enable_backups\",\"started_at\":\"2014-11-14T16:30:56Z\",\"completed_at\":null,\"resource_id\":3164494,\"resource_type\":\"droplet\",\"region\":{\"name\":\"New York 3\",\"slug\":\"nyc3\",\"sizes\":[\"s-1vcpu-3gb\",\"m-1vcpu-8gb\",\"s-3vcpu-1gb\",\"s-1vcpu-2gb\",\"s-2vcpu-2gb\",\"s-2vcpu-4gb\",\"s-4vcpu-8gb\",\"s-6vcpu-16gb\",\"s-8vcpu-32gb\",\"s-12vcpu-48gb\",\"s-16vcpu-64gb\",\"s-20vcpu-96gb\",\"s-1vcpu-1gb [...] + "headers": { + "Content-Type": "application/json; charset=UTF-8", + "Date": "Tue, 03 Nov 2020 09:39:11 GMT", + "Pragma": "no-cache", + "Expires": "Fri, 01 Jan 1990 00:00:00 GMT", + "Cache-Control": "no-cache, must-revalidate", + "Vary": "Accept-Language", + "Server": "mafe", + "X-XSS-Protection": "0", + "X-Frame-Options": "SAMEORIGIN", + "Server-Timing": "gfet4t7; dur=263", + "Alt-Svc": "h3-Q050=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-T050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"" + } + }, + "uuid": "09aa79df-8a19-41df-8655-e414d390d6ed", + "persistent": true, + "insertionIndex": 2 +} \ No newline at end of file diff --git a/integration-tests/digitalocean/src/test/resources/mappings/getDroplet.json b/integration-tests/digitalocean/src/test/resources/mappings/getDroplet.json new file mode 100644 index 0000000..1f68025 --- /dev/null +++ b/integration-tests/digitalocean/src/test/resources/mappings/getDroplet.json @@ -0,0 +1,28 @@ +{ + "id": "09aa79df-8a19-41df-8655-e414d390d6ec", + "name": "digitalocean_api_json", + "request": { + "url": "/V2/droplets/3164494?per_page=25", + "method": "GET" + }, + "response": { + "status": 200, + "body": "{\n \"droplet\": {\n \"id\": 3164494,\n \"name\": \"example.com\",\n \"memory\": 1024,\n \"vcpus\": 1,\n \"disk\": 25,\n \"locked\": true,\n \"status\": \"active\",\n \"kernel\": {\n \"id\": 2233,\n \"name\": \"Ubuntu 14.04 x64 vmlinuz-3.13.0-37-generic\",\n \"version\": \"3.13.0-37-generic\"\n },\n \"created_at\": \"2014-11-14T16:36:31Z\",\n \"features\": [\n \"virtio\"\n ],\n \"backup_ids\": [\n \n ],\n \"snapshot_ids\": [\n \n ],\n \"im [...] + "headers": { + "Content-Type": "application/json; charset=UTF-8", + "Date": "Tue, 03 Nov 2020 09:39:11 GMT", + "Pragma": "no-cache", + "Expires": "Fri, 01 Jan 1990 00:00:00 GMT", + "Cache-Control": "no-cache, must-revalidate", + "Vary": "Accept-Language", + "Server": "mafe", + "X-XSS-Protection": "0", + "X-Frame-Options": "SAMEORIGIN", + "Server-Timing": "gfet4t7; dur=263", + "Alt-Svc": "h3-Q050=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-T050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"" + } + }, + "uuid": "09aa79df-8a19-41df-8655-e414d390d6ec", + "persistent": true, + "insertionIndex": 2 +} \ No newline at end of file diff --git a/integration-tests/digitalocean/src/test/resources/mappings/kernelsDroplet.json b/integration-tests/digitalocean/src/test/resources/mappings/kernelsDroplet.json new file mode 100644 index 0000000..4fd9950 --- /dev/null +++ b/integration-tests/digitalocean/src/test/resources/mappings/kernelsDroplet.json @@ -0,0 +1,28 @@ +{ + "id": "09aa79df-8a19-41df-8655-e414d390d6ed", + "name": "digitalocean_api_json", + "request": { + "url": "/V2/droplets/3164494/kernels?page=1&per_page=25", + "method": "GET" + }, + "response": { + "status": 200, + "body" : "{\"kernels\":[{\"id\":231,\"name\":\"DO-recovery-static-fsck\",\"version\":\"3.8.0-25-generic\"}],\"links\":{\"pages\":{\"last\":\"https:\/\/api.digitalocean.com\/v2\/droplets\/3164494\/kernels?page=124&per_page=1\",\"next\":\"https:\/\/api.digitalocean.com\/v2\/droplets\/3164494\/kernels?page=2&per_page=1\"}},\"meta\":{\"total\":124}}", + "headers": { + "Content-Type": "application/json; charset=UTF-8", + "Date": "Tue, 03 Nov 2020 09:39:11 GMT", + "Pragma": "no-cache", + "Expires": "Fri, 01 Jan 1990 00:00:00 GMT", + "Cache-Control": "no-cache, must-revalidate", + "Vary": "Accept-Language", + "Server": "mafe", + "X-XSS-Protection": "0", + "X-Frame-Options": "SAMEORIGIN", + "Server-Timing": "gfet4t7; dur=263", + "Alt-Svc": "h3-Q050=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-T050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"" + } + }, + "uuid": "09aa79df-8a19-41df-8655-e414d390d6ed", + "persistent": true, + "insertionIndex": 2 +} \ No newline at end of file diff --git a/integration-tests/digitalocean/src/test/resources/mappings/neighborsDroplet.json b/integration-tests/digitalocean/src/test/resources/mappings/neighborsDroplet.json new file mode 100644 index 0000000..816399b --- /dev/null +++ b/integration-tests/digitalocean/src/test/resources/mappings/neighborsDroplet.json @@ -0,0 +1,28 @@ +{ + "id": "09aa79df-8a19-41df-8655-e414d390d6ed", + "name": "digitalocean_api_json", + "request": { + "url": "/V2/droplets/3164494/neighbors?page=1&per_page=25", + "method": "GET" + }, + "response": { + "status": 200, + "body" :"{\"droplets\":[{\"id\":3164495,\"name\":\"example.com\",\"memory\":1024,\"vcpus\":1,\"disk\":25,\"locked\":false,\"status\":\"active\",\"kernel\":{\"id\":2233,\"name\":\"Ubuntu 14.04 x64 vmlinuz-3.13.0-37-generic\",\"version\":\"3.13.0-37-generic\"},\"created_at\":\"2014-11-14T16:29:21Z\",\"features\":[\"backups\",\"ipv6\",\"virtio\"],\"backup_ids\":[7938002],\"snapshot_ids\":[],\"image\":{\"id\":6918990,\"name\":\"14.04 x64\",\"distribution\":\"Ubuntu\",\"slug\":\"ubuntu-16 [...] + "headers": { + "Content-Type": "application/json; charset=UTF-8", + "Date": "Tue, 03 Nov 2020 09:39:11 GMT", + "Pragma": "no-cache", + "Expires": "Fri, 01 Jan 1990 00:00:00 GMT", + "Cache-Control": "no-cache, must-revalidate", + "Vary": "Accept-Language", + "Server": "mafe", + "X-XSS-Protection": "0", + "X-Frame-Options": "SAMEORIGIN", + "Server-Timing": "gfet4t7; dur=263", + "Alt-Svc": "h3-Q050=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-T050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"" + } + }, + "uuid": "09aa79df-8a19-41df-8655-e414d390d6ed", + "persistent": true, + "insertionIndex": 2 +} \ No newline at end of file diff --git a/integration-tests/digitalocean/src/test/resources/mappings/snapshotsDroplet.json b/integration-tests/digitalocean/src/test/resources/mappings/snapshotsDroplet.json new file mode 100644 index 0000000..98c57f5 --- /dev/null +++ b/integration-tests/digitalocean/src/test/resources/mappings/snapshotsDroplet.json @@ -0,0 +1,28 @@ +{ + "id": "09aa79df-8a19-41df-8655-e414d390d6ed", + "name": "digitalocean_api_json", + "request": { + "url": "/V2/droplets/3164494/snapshots?page=1&per_page=25", + "method": "GET" + }, + "response": { + "status": 200, + "body" : "{\"snapshots\":[{\"id\":7938206,\"name\":\"snapshot1\",\"distribution\":\"Ubuntu\",\"slug\":null,\"public\":false,\"regions\":[\"nyc3\",\"nyc3\"],\"created_at\":\"2014-11-14T16:37:34Z\",\"type\":\"snapshot\",\"min_disk_size\":20,\"size_gigabytes\":2.34}],\"links\":{},\"meta\":{\"total\":1}}", + "headers": { + "Content-Type": "application/json; charset=UTF-8", + "Date": "Tue, 03 Nov 2020 09:39:11 GMT", + "Pragma": "no-cache", + "Expires": "Fri, 01 Jan 1990 00:00:00 GMT", + "Cache-Control": "no-cache, must-revalidate", + "Vary": "Accept-Language", + "Server": "mafe", + "X-XSS-Protection": "0", + "X-Frame-Options": "SAMEORIGIN", + "Server-Timing": "gfet4t7; dur=263", + "Alt-Svc": "h3-Q050=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-T050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"" + } + }, + "uuid": "09aa79df-8a19-41df-8655-e414d390d6ed", + "persistent": true, + "insertionIndex": 2 +} \ No newline at end of file diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 0b08eb5..2fdfafe 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -93,6 +93,7 @@ <module>dataformat</module> <module>dataformats-json</module> <module>debezium</module> + <module>digitalocean</module> <module>disruptor</module> <module>dozer</module> <module>dropbox</module> diff --git a/tooling/scripts/test-categories.yaml b/tooling/scripts/test-categories.yaml index c3a7137..cc52a5e 100644 --- a/tooling/scripts/test-categories.yaml +++ b/tooling/scripts/test-categories.yaml @@ -145,6 +145,7 @@ misc: - as2 - kotlin - atlasmap + - digitalocean - mail - main-unknown-args-fail - main-unknown-args-ignore