diff --git a/quarkus/addons/kubernetes/integration-tests/pom.xml b/quarkus/addons/kubernetes/integration-tests/pom.xml new file mode 100644 index 00000000000..51163e99297 --- /dev/null +++ b/quarkus/addons/kubernetes/integration-tests/pom.xml @@ -0,0 +1,133 @@ + + + 4.0.0 + + org.kie.kogito + kogito-addons-quarkus-kubernetes-parent + 2.0.0-SNAPSHOT + + + kogito-addons-quarkus-kubernetes-integration-tests + Kogito Add-On Kubernetes - Integration Tests + + + + + org.kie.kogito + kogito-quarkus-bom + ${project.version} + pom + import + + + + + + + io.quarkus + quarkus-cache + + + io.quarkus + quarkus-smallrye-openapi + + + org.kie.kogito + kogito-quarkus-serverless-workflow + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-resteasy-jackson + + + org.kie.kogito + kogito-addons-quarkus-kubernetes + + + org.kie.kogito + kogito-addons-quarkus-fabric8-kubernetes-service-catalog + + + io.quarkus + quarkus-kubernetes + + + + + io.quarkus + quarkus-junit5 + test + + + io.quarkus + quarkus-test-kubernetes-client + test + + + io.rest-assured + rest-assured + + + org.kie.kogito + kogito-addons-quarkus-fabric8-kubernetes-service-catalog-test-utils + test + + + + org.kie.kogito + kogito-quarkus-workflow-common-deployment + test + + + org.kie.kogito + kogito-addons-quarkus-messaging-deployment + test + + + + + + + io.quarkus + quarkus-maven-plugin + + true + ${skipTests} + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + + + + native + + + native + + + + native + + + + \ No newline at end of file diff --git a/quarkus/addons/kubernetes/integration-tests/src/main/java/org/kie/kogito/addons/quarkus/kubernetes/Foo.java b/quarkus/addons/kubernetes/integration-tests/src/main/java/org/kie/kogito/addons/quarkus/kubernetes/Foo.java new file mode 100644 index 00000000000..abcbda47995 --- /dev/null +++ b/quarkus/addons/kubernetes/integration-tests/src/main/java/org/kie/kogito/addons/quarkus/kubernetes/Foo.java @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates. + * + * Licensed 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.kie.kogito.addons.quarkus.kubernetes; + +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 org.eclipse.microprofile.config.inject.ConfigProperty; + +@Path("/foo") +@Produces(MediaType.TEXT_PLAIN) +public class Foo { + + @Inject + @ConfigProperty(name = "my_service") + String service; + + @GET + public String getWorkflowType() { + return service; + } +} diff --git a/quarkus/addons/kubernetes/integration-tests/src/main/resources/application.properties b/quarkus/addons/kubernetes/integration-tests/src/main/resources/application.properties new file mode 100644 index 00000000000..0979c469c2a --- /dev/null +++ b/quarkus/addons/kubernetes/integration-tests/src/main/resources/application.properties @@ -0,0 +1 @@ +my_service=${knative:services.v1.serving.knative.dev/default/serverless-workflow-greeting-quarkus}/path \ No newline at end of file diff --git a/quarkus/addons/kubernetes/integration-tests/src/test/java/org/kie/kogito/addons/quarkus/kubernetes/ConfigValueExpanderIT.java b/quarkus/addons/kubernetes/integration-tests/src/test/java/org/kie/kogito/addons/quarkus/kubernetes/ConfigValueExpanderIT.java new file mode 100644 index 00000000000..c22bb072b11 --- /dev/null +++ b/quarkus/addons/kubernetes/integration-tests/src/test/java/org/kie/kogito/addons/quarkus/kubernetes/ConfigValueExpanderIT.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates. + * + * Licensed 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.kie.kogito.addons.quarkus.kubernetes; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import io.fabric8.kubernetes.client.server.mock.KubernetesServer; +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.quarkus.test.kubernetes.client.KubernetesTestServer; +import io.quarkus.test.kubernetes.client.WithKubernetesTestServer; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; +import static org.kie.kogito.addons.quarkus.k8s.test.utils.KnativeResourceDiscoveryTestUtil.createServiceIfNotExists; + +@QuarkusIntegrationTest +@WithKubernetesTestServer +class ConfigValueExpanderIT { + + private static final String NAMESPACE = "default"; + + private static final String SERVICENAME = "serverless-workflow-greeting-quarkus"; + + @KubernetesTestServer + KubernetesServer mockServer; + + @BeforeEach + void beforeEach() { + createServiceIfNotExists(mockServer, "knative/quarkus-greeting.yaml", NAMESPACE, SERVICENAME); + } + + @Test + void test() { + given().when() + .get("/foo") + .then() + .statusCode(200) + .body(is("http://serverless-workflow-greeting-quarkus.test.10.99.154.147.sslip.io/path")); + } +} diff --git a/quarkus/addons/kubernetes/integration-tests/src/test/resources/knative/quarkus-greeting.yaml b/quarkus/addons/kubernetes/integration-tests/src/test/resources/knative/quarkus-greeting.yaml new file mode 100644 index 00000000000..712d182b28e --- /dev/null +++ b/quarkus/addons/kubernetes/integration-tests/src/test/resources/knative/quarkus-greeting.yaml @@ -0,0 +1,54 @@ +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + annotations: + serving.knative.dev/creator: minikube-user + serving.knative.dev/lastModifier: minikube-user + creationTimestamp: '2022-08-17T13:58:53Z' + generation: 1 + name: serverless-workflow-greeting-quarkus + resourceVersion: '43817' + uid: 98530cb6-3274-4d0c-b654-a82645cda058 +spec: + template: + metadata: + annotations: + client.knative.dev/updateTimestamp: '2022-08-17T13:58:53Z' + client.knative.dev/user-image: kiegroup/serverless-workflow-greeting-quarkus:1.0 + creationTimestamp: + spec: + containerConcurrency: 0 + containers: + - image: kiegroup/serverless-workflow-greeting-quarkus:1.0 + name: user-container + readinessProbe: + successThreshold: 1 + tcpSocket: + port: 0 + resources: { } + enableServiceLinks: false + timeoutSeconds: 300 + traffic: + - latestRevision: true + percent: 100 +status: + address: + url: http://serverless-workflow-greeting-quarkus.test.svc.cluster.local + conditions: + - lastTransitionTime: '2022-08-17T13:59:00Z' + status: 'True' + type: ConfigurationsReady + - lastTransitionTime: '2022-08-17T13:59:00Z' + status: 'True' + type: Ready + - lastTransitionTime: '2022-08-17T13:59:00Z' + status: 'True' + type: RoutesReady + latestCreatedRevisionName: serverless-workflow-greeting-quarkus-00001 + latestReadyRevisionName: serverless-workflow-greeting-quarkus-00001 + observedGeneration: 1 + traffic: + - latestRevision: true + percent: 100 + revisionName: serverless-workflow-greeting-quarkus-00001 + url: http://serverless-workflow-greeting-quarkus.test.10.99.154.147.sslip.io diff --git a/quarkus/addons/kubernetes/pom.xml b/quarkus/addons/kubernetes/pom.xml index 688556e4fd0..501c44cf22c 100644 --- a/quarkus/addons/kubernetes/pom.xml +++ b/quarkus/addons/kubernetes/pom.xml @@ -16,6 +16,7 @@ runtime deployment + integration-tests diff --git a/quarkus/addons/kubernetes/runtime/src/main/java/org/kie/kogito/addons/quarkus/k8s/config/ConfigValueExpander.java b/quarkus/addons/kubernetes/runtime/src/main/java/org/kie/kogito/addons/quarkus/k8s/config/ConfigValueExpander.java index 43fa7d04ce1..a45a13b7b61 100644 --- a/quarkus/addons/kubernetes/runtime/src/main/java/org/kie/kogito/addons/quarkus/k8s/config/ConfigValueExpander.java +++ b/quarkus/addons/kubernetes/runtime/src/main/java/org/kie/kogito/addons/quarkus/k8s/config/ConfigValueExpander.java @@ -15,6 +15,8 @@ */ package org.kie.kogito.addons.quarkus.k8s.config; +import java.util.Arrays; + import org.kie.kogito.addons.k8s.resource.catalog.KubernetesProtocol; import io.smallrye.config.ConfigValue; @@ -28,22 +30,42 @@ class ConfigValueExpander { } ConfigValue expand(ConfigValue configValue) { - if (configValue == null || !valueContainsDiscovery(configValue)) { - return configValue; + if (configValue != null && configValue.getRawValue() != null) { + String serviceCoordinates = extractServiceCoordinates(configValue.getRawValue()); + if (serviceCoordinates != null) { + return kubeDiscoveryConfigCache.get(configValue.getName(), serviceCoordinates) + .map(value -> interpolate(configValue.getRawValue(), value)) + .map(configValue::withValue) + .orElse(configValue); + } } - return kubeDiscoveryConfigCache.get(configValue.getName(), configValue.getValue()) - .map(configValue::withValue) - .orElse(configValue); + return configValue; + } + + public static String interpolate(String input, String replacement) { + int startIndex = input.indexOf("${"); + int endIndex = input.indexOf("}", startIndex); + + return input.substring(0, startIndex) + replacement + input.substring(endIndex + 1); } - private boolean valueContainsDiscovery(ConfigValue configValue) { - for (KubernetesProtocol protocol : KubernetesProtocol.values()) { - String value = configValue.getValue(); - if (value != null && value.startsWith(protocol.getValue() + ":")) { - return true; + static String extractServiceCoordinates(String rawValue) { + int startIndex = rawValue.indexOf("${"); + int endIndex = rawValue.indexOf("}", startIndex); + + if (startIndex != -1 && endIndex != -1) { + String substring = rawValue.substring(startIndex + 2, endIndex); + + boolean isKubernetesServiceCoordinate = Arrays.stream(KubernetesProtocol.values()) + .map(KubernetesProtocol::getValue) + .anyMatch(protocol -> substring.startsWith(protocol + ":")); + + if (isKubernetesServiceCoordinate) { + return substring; } } - return false; + + return null; } } diff --git a/quarkus/addons/kubernetes/runtime/src/test/java/org/kie/kogito/addons/quarkus/k8s/config/ConfigValueExpanderTest.java b/quarkus/addons/kubernetes/runtime/src/test/java/org/kie/kogito/addons/quarkus/k8s/config/ConfigValueExpanderTest.java index 73cc4e0709d..f9d3e0330d1 100644 --- a/quarkus/addons/kubernetes/runtime/src/test/java/org/kie/kogito/addons/quarkus/k8s/config/ConfigValueExpanderTest.java +++ b/quarkus/addons/kubernetes/runtime/src/test/java/org/kie/kogito/addons/quarkus/k8s/config/ConfigValueExpanderTest.java @@ -16,9 +16,12 @@ package org.kie.kogito.addons.quarkus.k8s.config; import java.util.Optional; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import io.smallrye.config.ConfigValue; @@ -27,32 +30,46 @@ class ConfigValueExpanderTest { + static Stream extractServiceCoordinatesSource() { + return Stream.of( + Arguments.of("${kubernetes:pods.v1/kie/kogito}/path", "kubernetes:pods.v1/kie/kogito"), + Arguments.of("${knative:services.v1.serving.knative.dev/default/serverless-workflow-greeting-quarkus}/path", + "knative:services.v1.serving.knative.dev/default/serverless-workflow-greeting-quarkus")); + } + + @ParameterizedTest + @MethodSource("extractServiceCoordinatesSource") + void extractServiceCoordinates(String expandableValue, String expectedCoordinate) { + assertThat(ConfigValueExpander.extractServiceCoordinates(expandableValue)) + .isEqualTo(expectedCoordinate); + } + @ParameterizedTest - @ValueSource(strings = { "kubernetes:pods.v1/kie/kogito", "openshift:pods.v1/kie/kogito", "knative:kie/kogito" }) + @ValueSource(strings = { "${kubernetes:pods.v1/kie/kogito}/path", "${openshift:pods.v1/kie/kogito}/path", "${knative:kie/kogito}/path" }) void expandable(String expandableValues) { - String expectedValue = "https://localhost/8080"; + String expectedUrl = "https://localhost/8080"; + String expectedValue = expectedUrl + "/path"; - ConfigValueExpander expander = new ConfigValueExpander(new FakeKubeDiscoveryConfigCache(expectedValue)); + ConfigValueExpander expander = new ConfigValueExpander(new FakeKubeDiscoveryConfigCache(expectedUrl)); ConfigValue configValue = ConfigValue.builder() - .withValue(expandableValues) + .withRawValue(expandableValues) .build(); assertThat(expander.expand(configValue).getValue()) .isEqualTo(expectedValue); } - @Test - void nonExpandable() { - String nonExpandableValue = "https://localhost/8080"; - + @ParameterizedTest + @ValueSource(strings = { "https://localhost/8080", "kubernetes:pods.v1/kie/kogito", "openshift:pods.v1/kie/kogito", "knative:kie/kogito", "${something}" }) + void nonExpandable(String nonExpandableValue) { ConfigValueExpander expander = new ConfigValueExpander(new FakeKubeDiscoveryConfigCache("should not be returned")); ConfigValue configValue = ConfigValue.builder() - .withValue(nonExpandableValue) + .withRawValue(nonExpandableValue) .build(); - assertThat(expander.expand(configValue).getValue()) + assertThat(expander.expand(configValue).getRawValue()) .isEqualTo(nonExpandableValue); } @@ -61,7 +78,7 @@ void nullShouldBeNonExpandable() { ConfigValueExpander expander = new ConfigValueExpander(new FakeKubeDiscoveryConfigCache("should not be returned")); ConfigValue configValue = ConfigValue.builder() - .withValue(null) + .withRawValue(null) .build(); assertThat(expander.expand(configValue).getValue())