aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xexamples/src/main/resources/clamp/acm/test/participant-kserve.yaml138
-rwxr-xr-x[-rw-r--r--]packages/policy-clamp-docker/pom.xml38
-rwxr-xr-xpackages/policy-clamp-docker/src/main/docker/KserveParticipant-Suse.Dockerfile66
-rwxr-xr-xpackages/policy-clamp-docker/src/main/docker/KserveParticipant.Dockerfile58
-rwxr-xr-xpackages/policy-clamp-docker/src/main/docker/kserve-participant.sh59
-rwxr-xr-x[-rw-r--r--]packages/policy-clamp-tarball/pom.xml5
-rwxr-xr-xpackages/policy-clamp-tarball/src/main/resources/etc/KserveParticipantParameters.yaml57
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/pom.xml67
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/Application.java61
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/config/MicrometerConfig.java46
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/config/ParticipantConfig.java43
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/config/SecurityConfig.java48
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/exception/KserveException.java39
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/handler/AutomationCompositionElementHandler.java167
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/k8s/InferenceServiceValidator.java102
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/k8s/KserveClient.java112
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/models/ConfigurationEntity.java37
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/models/KserveInferenceEntity.java49
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/parameters/CustomResourceDefinitionParameters.java43
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/parameters/KserveParticipantParameters.java44
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/main/resources/config/application.yaml65
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/handler/AcElementHandlerTest.java125
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/k8s/InferenceServiceValidatorTest.java78
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/k8s/KserveClientTest.java196
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/rest/ActuatorControllerTest.java97
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/utils/CommonActuatorController.java107
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/utils/CommonTestData.java60
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/utils/ToscaUtils.java51
-rwxr-xr-xparticipant/participant-impl/participant-impl-kserve/src/test/resources/application-test.yaml17
-rwxr-xr-x[-rw-r--r--]participant/participant-impl/pom.xml1
-rwxr-xr-x[-rw-r--r--]pom.xml1
31 files changed, 2077 insertions, 0 deletions
diff --git a/examples/src/main/resources/clamp/acm/test/participant-kserve.yaml b/examples/src/main/resources/clamp/acm/test/participant-kserve.yaml
new file mode 100755
index 000000000..14eb13d6a
--- /dev/null
+++ b/examples/src/main/resources/clamp/acm/test/participant-kserve.yaml
@@ -0,0 +1,138 @@
+# ============LICENSE_START=======================================================
+# Copyright (C) 2023 Nordix Foundation.
+# ================================================================================
+# 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+tosca_definitions_version: tosca_simple_yaml_1_3
+data_types:
+ onap.datatypes.ToscaConceptIdentifier:
+ derived_from: tosca.datatypes.Root
+ properties:
+ name:
+ type: string
+ required: true
+ version:
+ type: string
+ required: true
+ org.onap.datatypes.policy.clamp.acm.kserveAutomationCompositionElement.KserveInferenceEntity:
+ version: 1.0.0
+ derived_from: tosca.datatypes.Root
+ properties:
+ kserveEntityId:
+ type: onap.datatypes.ToscaConceptIdentifier
+ type_version: 1.0.0
+ required: true
+ description: The name and version of a Configuration Entity to be handled by the Kserve Automation Composition Element
+ name:
+ type: string
+ required: true
+ description: Inference service name to be created
+ payload:
+ type: string
+ required: true
+ description: Inference Service payload
+
+node_types:
+ org.onap.policy.clamp.acm.Participant:
+ version: 1.0.1
+ derived_from: tosca.nodetypes.Root
+ properties:
+ provider:
+ type: string
+ required: false
+ org.onap.policy.clamp.acm.AutomationCompositionElement:
+ version: 1.0.1
+ derived_from: tosca.nodetypes.Root
+ properties:
+ provider:
+ type: string
+ required: false
+ participantType:
+ type: onap.datatypes.ToscaConceptIdentifier
+ required: true
+ startPhase:
+ type: integer
+ required: false
+ constraints:
+ - greater-or-equal: 0
+ metadata:
+ common: true
+ description: A value indicating the start phase in which this automation composition element will be started, the
+ first start phase is zero. Automation Composition Elements are started in their start_phase order and stopped
+ in reverse start phase order. Automation Composition Elements with the same start phase are started and
+ stopped simultaneously
+ org.onap.policy.clamp.acm.AutomationComposition:
+ version: 1.0.1
+ derived_from: tosca.nodetypes.Root
+ properties:
+ provider:
+ type: string
+ required: false
+ elements:
+ type: list
+ required: true
+ entry_schema:
+ type: onap.datatypes.ToscaConceptIdentifier
+ org.onap.policy.clamp.acm.KserveAutomationCompositionElement:
+ version: 1.0.1
+ derived_from: org.onap.policy.clamp.acm.AutomationCompositionElement
+ properties:
+ kserveInferenceEntities:
+ type: list
+ required: true
+ entry_schema:
+ type: org.onap.datatypes.policy.clamp.acm.kserveAutomationCompositionElement.KserveInferenceEntityId
+ type_version: 1.0.0
+ description: The configuration entities of Kserve inference service
+topology_template:
+ node_templates:
+ org.onap.policy.clamp.acm.KserveParticipant:
+ version: 2.3.4
+ type: org.onap.policy.clamp.acm.Participant
+ type_version: 1.0.1
+ description: Participant for Kserve requests
+ properties:
+ provider: ONAP
+
+ onap.policy.clamp.ac.element.KserveAutomationCompositionElement:
+ version: 1.2.3
+ type: org.onap.policy.clamp.acm.KserveAutomationCompositionElement
+ type_version: 1.0.1
+ description: Automation composition element for the Kserve Requests
+ properties:
+ provider: ONAP
+ participantType:
+ name: org.onap.policy.clamp.acm.KserveParticipant
+ version: 2.3.4
+ uninitializedToPassiveTimeout: 60
+ statusCheckInterval: 30
+ kserveInferenceEntities:
+ - kserveInferenceEntityId:
+ name: entity1
+ version: 1.0.1
+ name: sklearn-iris
+ namespace: kserve-test
+ payload: '{"apiVersion": "serving.kserve.io/v1beta1","kind": "InferenceService","metadata": {"name": "sklearn-iris"},"spec": {"predictor": {"model":{"modelFormat": {"name": "sklearn"},"storageUri": "gs://kfserving-examples/models/sklearn/1.0/model"}}}}'
+
+ onap.policy.clamp.ac.element.AutomationCompositionDefinition:
+ version: 1.2.3
+ type: org.onap.policy.clamp.acm.AutomationComposition
+ type_version: 1.0.0
+ description: Automation composition for Kserve request
+ properties:
+ provider: ONAP
+ elements:
+ - name: onap.policy.clamp.ac.element.KserveAutomationCompositionElement
+ version: 1.2.3
diff --git a/packages/policy-clamp-docker/pom.xml b/packages/policy-clamp-docker/pom.xml
index 576f3d057..1b9b5e1fd 100644..100755
--- a/packages/policy-clamp-docker/pom.xml
+++ b/packages/policy-clamp-docker/pom.xml
@@ -71,6 +71,11 @@
</dependency>
<dependency>
<groupId>org.onap.policy.clamp.participant</groupId>
+ <artifactId>policy-clamp-participant-impl-kserve</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.clamp.participant</groupId>
<artifactId>policy-clamp-participant-impl-a1pms</artifactId>
<version>${project.version}</version>
</dependency>
@@ -282,6 +287,39 @@
</build>
</image>
<image>
+ <name>onap/policy-clamp-ac-kserve-ppnt</name>
+ <alias>onap-policy-clamp-ac-kserve-ppnt</alias>
+ <build>
+ <cleanup>try</cleanup>
+ <dockerFile>${docker.clamp-ac-kserve-ppnt.dockerFile}</dockerFile>
+ <contextDir>${docker.clamp-ac-kserve-ppnt.contextDir}</contextDir>
+ <tags>
+ <tag>${project.version}</tag>
+ <tag>${project.version}-${maven.build.timestamp}</tag>
+ <tag>${project.docker.latest.minmax.tag.version}</tag>
+ </tags>
+ <assembly>
+ <inline>
+ <dependencySets>
+ <dependencySet>
+ <includes>
+ <include>org.onap.policy.clamp:policy-clamp-tarball</include>
+ </includes>
+ <outputDirectory>/lib</outputDirectory>
+ <outputFileNameMapping>kserve-participant.tar.gz</outputFileNameMapping>
+ </dependencySet>
+ <dependencySet>
+ <includes>
+ <include>org.onap.policy.clamp.participant:policy-clamp-participant-impl-kserve</include>
+ </includes>
+ <outputFileNameMapping>policy-clamp-participant-impl-kserve.jar</outputFileNameMapping>
+ </dependencySet>
+ </dependencySets>
+ </inline>
+ </assembly>
+ </build>
+ </image>
+ <image>
<name>onap/policy-clamp-acm-element-impl</name>
<alias>onap-policy-clamp-acm-element-impl</alias>
<build>
diff --git a/packages/policy-clamp-docker/src/main/docker/KserveParticipant-Suse.Dockerfile b/packages/policy-clamp-docker/src/main/docker/KserveParticipant-Suse.Dockerfile
new file mode 100755
index 000000000..df8603d70
--- /dev/null
+++ b/packages/policy-clamp-docker/src/main/docker/KserveParticipant-Suse.Dockerfile
@@ -0,0 +1,66 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START=======================================================
+# Copyright (C) 2023 Nordix Foundation.
+# ================================================================================
+# 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+#-------------------------------------------------------------------------------
+
+FROM busybox AS tarball
+RUN mkdir /packages /extracted
+COPY /maven/lib/kserve-participant.tar.gz /packages/
+RUN tar xvzf /packages/kserve-participant.tar.gz --directory /extracted/
+
+FROM opensuse/leap:15.4
+
+LABEL maintainer="Policy Team"
+LABEL org.opencontainers.image.title="Policy CLAMP ACM KSERVE Participant"
+LABEL org.opencontainers.image.description="Policy CLAMP ACM KSERVE Participant image based on OpenSuse"
+LABEL org.opencontainers.image.url="https://github.com/onap/policy-clamp"
+LABEL org.opencontainers.image.vendor="ONAP Policy Team"
+LABEL org.opencontainers.image.licenses="Apache-2.0"
+LABEL org.opencontainers.image.created="${git.build.time}"
+LABEL org.opencontainers.image.version="${git.build.version}"
+LABEL org.opencontainers.image.revision="${git.commit.id.abbrev}"
+
+ARG POLICY_LOGS=/var/log/onap/policy/kserve-participant
+
+ENV POLICY_LOGS=$POLICY_LOGS
+ENV POLICY_HOME=/opt/app/policy/clamp
+ENV LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=en_US.UTF-8
+ENV JAVA_HOME=/usr/lib64/jvm/java-11-openjdk-11
+
+RUN zypper -n -q install --no-recommends gzip java-11-openjdk-headless netcat-openbsd tar wget && \
+ zypper -n -q update && zypper -n -q clean --all && \
+ groupadd --system policy && \
+ useradd --system --shell /bin/sh -G policy policy && \
+ mkdir -p /app $POLICY_HOME $POLICY_LOGS && \
+ chown -R policy:policy /app $POLICY_HOME $POLICY_LOGS
+
+COPY --chown=policy:policy --from=tarball /extracted $POLICY_HOME
+
+WORKDIR $POLICY_HOME
+COPY --chown=policy:policy kubernetes-participant.sh bin/
+COPY --chown=policy:policy /maven/policy-clamp-participant-impl-kserve.jar /app/app.jar
+
+RUN chmod 755 bin/*.sh
+
+EXPOSE 8087
+
+USER policy
+WORKDIR $POLICY_HOME/bin
+ENTRYPOINT [ "./kserve-participant.sh" ]
+
+
diff --git a/packages/policy-clamp-docker/src/main/docker/KserveParticipant.Dockerfile b/packages/policy-clamp-docker/src/main/docker/KserveParticipant.Dockerfile
new file mode 100755
index 000000000..de60cc805
--- /dev/null
+++ b/packages/policy-clamp-docker/src/main/docker/KserveParticipant.Dockerfile
@@ -0,0 +1,58 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START=======================================================
+# Copyright (C) 2023 Nordix Foundation.
+# ================================================================================
+# 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+#-------------------------------------------------------------------------------
+
+FROM busybox AS tarball
+RUN mkdir /packages /extracted
+COPY /maven/lib/kserve-participant.tar.gz /packages/
+RUN tar xvzf /packages/kserve-participant.tar.gz --directory /extracted/
+
+FROM onap/policy-jre-alpine:2.5.2-SNAPSHOT
+
+LABEL maintainer="Policy Team"
+LABEL org.opencontainers.image.title="Policy CLAMP ACM KSERVE Participant"
+LABEL org.opencontainers.image.description="Policy CLAMP ACM Kserve Participant image based on Alpine"
+LABEL org.opencontainers.image.url="https://github.com/onap/policy-clamp"
+LABEL org.opencontainers.image.vendor="ONAP Policy Team"
+LABEL org.opencontainers.image.licenses="Apache-2.0"
+LABEL org.opencontainers.image.created="${git.build.time}"
+LABEL org.opencontainers.image.version="${git.build.version}"
+LABEL org.opencontainers.image.revision="${git.commit.id.abbrev}"
+
+ARG POLICY_LOGS=/var/log/onap/policy/kserve-participant
+
+ENV POLICY_LOGS=$POLICY_LOGS
+ENV POLICY_HOME=$POLICY_HOME/clamp
+
+RUN mkdir -p $POLICY_HOME $POLICY_LOGS && \
+ chown -R policy:policy $POLICY_HOME $POLICY_LOGS
+
+COPY --chown=policy:policy --from=tarball /extracted $POLICY_HOME
+
+WORKDIR $POLICY_HOME
+COPY --chown=policy:policy kserve-participant.sh bin/
+COPY --chown=policy:policy /maven/policy-clamp-participant-impl-kserve.jar /app/app.jar
+
+RUN chmod 755 bin/*.sh
+
+EXPOSE 8087
+
+USER policy
+WORKDIR $POLICY_HOME/bin
+ENTRYPOINT [ "./kserve-participant.sh" ]
diff --git a/packages/policy-clamp-docker/src/main/docker/kserve-participant.sh b/packages/policy-clamp-docker/src/main/docker/kserve-participant.sh
new file mode 100755
index 000000000..cb88db4be
--- /dev/null
+++ b/packages/policy-clamp-docker/src/main/docker/kserve-participant.sh
@@ -0,0 +1,59 @@
+#!/usr/bin/env sh
+#
+# ============LICENSE_START=======================================================
+# Copyright (C) 2023 Nordix Foundation.
+# ================================================================================
+# 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+#
+
+KEYSTORE="${KEYSTORE:-$POLICY_HOME/etc/ssl/policy-keystore}"
+TRUSTSTORE="${TRUSTSTORE:-$POLICY_HOME/etc/ssl/policy-truststore}"
+KEYSTORE_PASSWD="${KEYSTORE_PASSWD:-Pol1cy_0nap}"
+TRUSTSTORE_PASSWD="${TRUSTSTORE_PASSWD:-Pol1cy_0nap}"
+
+if [ "$#" -eq 1 ]; then
+ CONFIG_FILE=$1
+fi
+
+if [ -z "$CONFIG_FILE" ]; then
+ CONFIG_FILE="${POLICY_HOME}/etc/KserveParticipantParameters.yaml"
+fi
+
+echo "Policy clamp Kserve participant config file: $CONFIG_FILE"
+
+if [ -f "${POLICY_HOME}/etc/mounted/policy-truststore" ]; then
+ echo "overriding policy-truststore"
+ cp -f "${POLICY_HOME}"/etc/mounted/policy-truststore "${TRUSTSTORE}"
+fi
+
+if [ -f "${POLICY_HOME}/etc/mounted/policy-keystore" ]; then
+ echo "overriding policy-keystore"
+ cp -f "${POLICY_HOME}"/etc/mounted/policy-keystore "${KEYSTORE}"
+fi
+
+if [ -f "${POLICY_HOME}/etc/mounted/logback.xml" ]; then
+ echo "overriding logback xml file"
+ cp -f "${POLICY_HOME}"/etc/mounted/logback.xml "${POLICY_HOME}"/etc/
+fi
+
+$JAVA_HOME/bin/java \
+ -Dlogging.config="${POLICY_HOME}/etc/logback.xml" \
+ -Dserver.ssl.keyStore="${KEYSTORE}" \
+ -Dserver.ssl.keyStorePassword="${KEYSTORE_PASSWD}" \
+ -Djavax.net.ssl.trustStore="${TRUSTSTORE}" \
+ -Djavax.net.ssl.trustStorePassword="${TRUSTSTORE_PASSWD}" \
+ -jar /app/app.jar \
+ --spring.config.location="${CONFIG_FILE}"
diff --git a/packages/policy-clamp-tarball/pom.xml b/packages/policy-clamp-tarball/pom.xml
index b1349dcc3..4a242ec19 100644..100755
--- a/packages/policy-clamp-tarball/pom.xml
+++ b/packages/policy-clamp-tarball/pom.xml
@@ -54,6 +54,11 @@
</dependency>
<dependency>
<groupId>org.onap.policy.clamp.participant</groupId>
+ <artifactId>policy-clamp-participant-impl-kserve</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.clamp.participant</groupId>
<artifactId>policy-clamp-participant-impl-a1pms</artifactId>
<version>${project.version}</version>
</dependency>
diff --git a/packages/policy-clamp-tarball/src/main/resources/etc/KserveParticipantParameters.yaml b/packages/policy-clamp-tarball/src/main/resources/etc/KserveParticipantParameters.yaml
new file mode 100755
index 000000000..169217841
--- /dev/null
+++ b/packages/policy-clamp-tarball/src/main/resources/etc/KserveParticipantParameters.yaml
@@ -0,0 +1,57 @@
+spring:
+ cloud:
+ kubernetes:
+ enabled: false
+ discovery:
+ enabled: false
+ security:
+ user:
+ name: participantUser
+ password: zb!XztG34
+ autoconfigure:
+ exclude:
+ - org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
+ - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
+ - org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
+ - org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration
+ - io.kubernetes.client.spring.extended.manifests.config.KubernetesManifestsAutoConfiguration
+ - io.kubernetes.client.spring.extended.network.config.KubernetesLoadBalancerAutoConfiguration
+security:
+ enable-csrf: false
+
+participant:
+ intermediaryParameters:
+ reportingTimeIntervalMs: 120000
+ description: Participant Description
+ participantId: 101c62b3-8918-41b9-a747-d21eb79c6c04
+ clampAutomationCompositionTopics:
+ topicSources:
+ - topic: POLICY-ACRUNTIME-PARTICIPANT
+ servers:
+ - ${topicServer:message-router}
+ topicCommInfrastructure: dmaap
+ fetchTimeout: 15000
+ topicSinks:
+ - topic: POLICY-ACRUNTIME-PARTICIPANT
+ servers:
+ - ${topicServer:message-router}
+ topicCommInfrastructure: dmaap
+
+customresourcedefinition:
+ group: serving.kserve.io
+ version: v1beta1
+ plural: inferenceservices
+ grace-period: 10
+
+management:
+ endpoints:
+ web:
+ base-path: /
+ exposure:
+ include: health, metrics, prometheus
+server:
+ port: 8087
+ servlet:
+ context-path: /onap/policy/clamp/acm/kserveparticipant
+
+
diff --git a/participant/participant-impl/participant-impl-kserve/pom.xml b/participant/participant-impl/participant-impl-kserve/pom.xml
new file mode 100755
index 000000000..81305006a
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/pom.xml
@@ -0,0 +1,67 @@
+<!--
+ ============LICENSE_START=======================================================
+ Copyright (C) 2023 Nordix Foundation.
+ ================================================================================
+ 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.
+
+ SPDX-License-Identifier: Apache-2.0
+ ============LICENSE_END=========================================================
+-->
+
+<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.onap.policy.clamp.participant</groupId>
+ <artifactId>policy-clamp-participant-impl</artifactId>
+ <version>6.4.1-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>policy-clamp-participant-impl-kserve</artifactId>
+ <name>${project.artifactId}</name>
+ <description>Kserve participant, that performs operations related to Kserve inference service deployment</description>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ <phase>package</phase>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-dependencies</artifactId>
+ <version>2021.0.5</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-starter-kubernetes-client</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/Application.java b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/Application.java
new file mode 100755
index 000000000..c5299e235
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/Application.java
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve;
+
+import io.kubernetes.client.openapi.ApiClient;
+import io.kubernetes.client.util.Config;
+import java.io.IOException;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+
+/**
+ * Starter.
+ */
+// @formatter:off
+@SpringBootApplication
+@ComponentScan({"org.onap.policy.clamp.acm.participant.kserve", "org.onap.policy.clamp.acm.participant.intermediary"})
+@ConfigurationPropertiesScan("org.onap.policy.clamp.acm.participant.kserve.parameters")
+//@formatter:on
+public class Application {
+
+ /**
+ * Main class.
+ *
+ * @param args args
+ */
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+ /**
+ * Default Api Client bean creation.
+ *
+ * @return ApiClient
+ * @throws IOException exception
+ */
+ @Bean
+ public ApiClient defaultApiClient() throws IOException {
+ return Config.fromCluster();
+ }
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/config/MicrometerConfig.java b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/config/MicrometerConfig.java
new file mode 100755
index 000000000..92dc5d3d6
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/config/MicrometerConfig.java
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.config;
+
+import io.micrometer.core.aop.TimedAspect;
+import io.micrometer.core.instrument.MeterRegistry;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class MicrometerConfig {
+
+ /**
+ * Load up the metrics registry.
+ */
+ @Bean
+ public InitializingBean forcePrometheusPostProcessor(BeanPostProcessor meterRegistryPostProcessor,
+ MeterRegistry registry) {
+ return () -> meterRegistryPostProcessor.postProcessAfterInitialization(registry, "");
+ }
+
+ @Bean
+ public TimedAspect timedAspect(MeterRegistry registry) {
+ return new TimedAspect(registry);
+ }
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/config/ParticipantConfig.java b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/config/ParticipantConfig.java
new file mode 100755
index 000000000..1a671343a
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/config/ParticipantConfig.java
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.config;
+
+import org.onap.policy.clamp.acm.participant.intermediary.api.ParticipantIntermediaryApi;
+import org.onap.policy.clamp.acm.participant.kserve.handler.AutomationCompositionElementHandler;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class ParticipantConfig {
+
+ /**
+ * Register AutomationCompositionElementListener.
+ *
+ * @param intermediaryApi the ParticipantIntermediaryApi
+ * @param acElementHandler the AutomationComposition Element Handler
+ */
+ @Autowired
+ public void registerAutomationCompositionElementListener(ParticipantIntermediaryApi intermediaryApi,
+ AutomationCompositionElementHandler acElementHandler) {
+ intermediaryApi.registerAutomationCompositionElementListener(acElementHandler);
+ acElementHandler.setIntermediaryApi(intermediaryApi);
+ }
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/config/SecurityConfig.java b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/config/SecurityConfig.java
new file mode 100755
index 000000000..f9dbc08fc
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/config/SecurityConfig.java
@@ -0,0 +1,48 @@
+/*-
+ * ========================LICENSE_START=================================
+ * Copyright (C) 2023 Nordix Foundation. All rights reserved.
+ * ======================================================================
+ * 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.
+ * ========================LICENSE_END===================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.web.SecurityFilterChain;
+
+@Configuration
+public class SecurityConfig {
+
+ /**
+ * Return the configuration of how access to this module's REST end points is secured.
+ *
+ * @param http the HTTP security settings
+ * @return the HTTP security settings
+ */
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+ http
+ .httpBasic()
+ .and()
+ .authorizeHttpRequests()
+ .anyRequest()
+ .authenticated()
+ .and()
+ .csrf()
+ .disable();
+ return http.build();
+ }
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/exception/KserveException.java b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/exception/KserveException.java
new file mode 100755
index 000000000..045c80c21
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/exception/KserveException.java
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.exception;
+
+import javax.ws.rs.core.Response;
+import org.onap.policy.models.base.PfModelException;
+
+public class KserveException extends PfModelException {
+
+ public KserveException(String message) {
+ super(Response.Status.BAD_GATEWAY, message);
+ }
+
+ public KserveException(int statusCode, String message, Exception e) {
+ super(Response.Status.fromStatusCode(statusCode), message, e);
+ }
+
+ public KserveException(String message, Exception originalException) {
+ super(Response.Status.BAD_GATEWAY, message, originalException);
+ }
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/handler/AutomationCompositionElementHandler.java b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/handler/AutomationCompositionElementHandler.java
new file mode 100755
index 000000000..cae70565f
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/handler/AutomationCompositionElementHandler.java
@@ -0,0 +1,167 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.handler;
+
+import io.kubernetes.client.openapi.ApiException;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import javax.validation.Validation;
+import javax.validation.ValidationException;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import org.apache.http.HttpStatus;
+import org.onap.policy.clamp.acm.participant.intermediary.api.AutomationCompositionElementListener;
+import org.onap.policy.clamp.acm.participant.intermediary.api.ParticipantIntermediaryApi;
+import org.onap.policy.clamp.acm.participant.kserve.exception.KserveException;
+import org.onap.policy.clamp.acm.participant.kserve.k8s.InferenceServiceValidator;
+import org.onap.policy.clamp.acm.participant.kserve.k8s.KserveClient;
+import org.onap.policy.clamp.acm.participant.kserve.models.ConfigurationEntity;
+import org.onap.policy.clamp.acm.participant.kserve.models.KserveInferenceEntity;
+import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
+import org.onap.policy.clamp.models.acm.concepts.DeployState;
+import org.onap.policy.clamp.models.acm.concepts.LockState;
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.models.base.PfModelException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * This class handles implementation of automationCompositionElement updates.
+ */
+@Component
+@RequiredArgsConstructor
+public class AutomationCompositionElementHandler implements AutomationCompositionElementListener {
+
+ private static final Coder CODER = new StandardCoder();
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ private ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
+
+ @Setter
+ private ParticipantIntermediaryApi intermediaryApi;
+
+ private final KserveClient kserveClient;
+
+ @Getter
+ private static final Map<UUID, ConfigurationEntity> configRequestMap = new HashMap<>();
+
+
+ private static class ThreadConfig {
+
+ private int uninitializedToPassiveTimeout = 60;
+ private int statusCheckInterval = 30;
+ }
+
+ @Override
+ public void undeploy(UUID automationCompositionId, UUID automationCompositionElementId) {
+ var configurationEntity = configRequestMap.get(automationCompositionElementId);
+ if (configurationEntity != null) {
+ try {
+ for (KserveInferenceEntity kserveInferenceEntity : configurationEntity.getKserveInferenceEntities()) {
+ kserveClient.undeployInferenceService(kserveInferenceEntity.getNamespace(),
+ kserveInferenceEntity.getName());
+ }
+ configRequestMap.remove(automationCompositionElementId);
+ intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
+ automationCompositionElementId, DeployState.UNDEPLOYED, LockState.NONE);
+ } catch (IOException | ApiException exception) {
+ LOGGER.warn("Deletion of Inference service failed", exception);
+ }
+ }
+ }
+
+ /**
+ * Callback method to handle an update on an automation composition element.
+ *
+ * @param automationCompositionId the ID of the automation composition
+ * @param element the information on the automation composition element
+ * @param properties properties Map
+ */
+ @Override
+ public void deploy(UUID automationCompositionId, AcElementDeploy element, Map<String, Object> properties)
+ throws PfModelException {
+ try {
+ var configurationEntity = CODER.convert(properties, ConfigurationEntity.class);
+ var violations = Validation.buildDefaultValidatorFactory().getValidator().validate(configurationEntity);
+ if (violations.isEmpty()) {
+ boolean isAllInferenceSvcDeployed = true;
+ var config = CODER.convert(properties, ThreadConfig.class);
+ for (KserveInferenceEntity kserveInferenceEntity : configurationEntity.getKserveInferenceEntities()) {
+ kserveClient.deployInferenceService(kserveInferenceEntity.getNamespace(),
+ kserveInferenceEntity.getPayload());
+
+ if (!checkInferenceServiceStatus(kserveInferenceEntity.getName(),
+ kserveInferenceEntity.getNamespace(), config.uninitializedToPassiveTimeout,
+ config.statusCheckInterval)) {
+ isAllInferenceSvcDeployed = false;
+ break;
+ }
+ }
+ if (isAllInferenceSvcDeployed) {
+ configRequestMap.put(element.getId(), configurationEntity);
+ intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, element.getId(),
+ DeployState.DEPLOYED, LockState.LOCKED);
+ } else {
+ LOGGER.error("Inference Service deployment failed");
+ }
+ } else {
+ LOGGER.error("Violations found in the config request parameters: {}", violations);
+ throw new ValidationException("Constraint violations in the config request");
+ }
+ } catch (ValidationException | ExecutionException | InterruptedException | CoderException e) {
+ throw new KserveException(HttpStatus.SC_BAD_REQUEST, "Invalid Configuration", e);
+ } catch (IOException | ApiException e) {
+ throw new KserveException(HttpStatus.SC_BAD_REQUEST, "Failed to configure the inference service", e);
+ }
+ }
+
+ /**
+ * Check the status of Inference Service.
+ *
+ * @param inferenceServiceName name of the inference service
+ * @param namespace kubernetes namespace
+ * @param timeout Inference service time check
+ * @param statusCheckInterval Status check time interval
+ * @return status of the inference service
+ * @throws ExecutionException Exception on execution
+ * @throws InterruptedException Exception on inference service status check
+ */
+ public boolean checkInferenceServiceStatus(String inferenceServiceName, String namespace, int timeout,
+ int statusCheckInterval) throws ExecutionException, InterruptedException {
+ // Invoke runnable thread to check pod status
+ Future<String> result = executor.submit(
+ new InferenceServiceValidator(inferenceServiceName, namespace, timeout, statusCheckInterval,
+ kserveClient), "Done");
+ return (!result.get().isEmpty()) && result.isDone();
+ }
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/k8s/InferenceServiceValidator.java b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/k8s/InferenceServiceValidator.java
new file mode 100755
index 000000000..65c4c7e27
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/k8s/InferenceServiceValidator.java
@@ -0,0 +1,102 @@
+/*-
+ * ========================LICENSE_START=================================
+ * Copyright (C) 2023 Nordix Foundation. All rights reserved.
+ * ======================================================================
+ * 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.
+ * ========================LICENSE_END===================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.k8s;
+
+import io.kubernetes.client.openapi.ApiException;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import lombok.SneakyThrows;
+import org.onap.policy.clamp.acm.participant.kserve.exception.KserveException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class InferenceServiceValidator implements Runnable {
+
+ private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ private final KserveClient kserveClient;
+
+ private final int statusCheckInterval;
+
+ //Timeout for the thread to exit.
+ private final int timeout;
+
+ private final String inferenceServiceName;
+
+ private final String namespace;
+
+ /**
+ * Constructor for PodStatusValidator.
+ *
+ * @param inferenceServiceName name of the inference service
+ * @param namespace kubernetes namespace
+ * @param timeout timeout for the thread to exit
+ * @param statusCheckInterval Interval to check pod status
+ */
+ public InferenceServiceValidator(String inferenceServiceName, String namespace, int timeout,
+ int statusCheckInterval, KserveClient kserveClient) {
+ this.inferenceServiceName = inferenceServiceName;
+ this.namespace = namespace;
+ this.timeout = timeout;
+ this.statusCheckInterval = statusCheckInterval;
+ this.kserveClient = kserveClient;
+ }
+
+
+ @SneakyThrows
+ @Override
+ public void run() {
+ logger.info("Polling the status of deployed Inference Service {} in namespace {}", inferenceServiceName,
+ namespace);
+ try {
+ verifyInferenceServiceStatus();
+ } catch (KserveException | IOException e) {
+ throw new KserveException("Error verifying the status of the inference service. Exiting", e);
+ }
+ }
+
+ /**
+ * Verify inference service status.
+ * @throws KserveException exception
+ * @throws IOException exception
+ * @throws InterruptedException exception
+ * @throws ApiException exception
+ */
+ private void verifyInferenceServiceStatus()
+ throws KserveException, IOException, InterruptedException, ApiException {
+ var isVerified = false;
+ long endTime = System.currentTimeMillis() + (timeout * 1000L);
+
+ while (!isVerified && System.currentTimeMillis() < endTime) {
+ var output = kserveClient.getInferenceServiceStatus(namespace, inferenceServiceName);
+ isVerified = output.equalsIgnoreCase(Boolean.TRUE.toString());
+ if (!isVerified) {
+ logger.info("Waiting for the inference service {} to be active ", inferenceServiceName);
+ // Recheck status of pods in specific intervals.
+ Thread.sleep(statusCheckInterval * 1000L);
+ } else {
+ logger.info("Inference Service {} is Ready to use ", inferenceServiceName);
+ }
+ }
+ if (!isVerified) {
+ throw new KserveException("Time out Exception verifying the status of the inference service");
+ }
+ }
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/k8s/KserveClient.java b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/k8s/KserveClient.java
new file mode 100755
index 000000000..b5e7892eb
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/k8s/KserveClient.java
@@ -0,0 +1,112 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.k8s;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.kubernetes.client.openapi.ApiClient;
+import io.kubernetes.client.openapi.ApiException;
+import io.kubernetes.client.openapi.apis.CustomObjectsApi;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import javax.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import okhttp3.Response;
+import org.onap.policy.clamp.acm.participant.kserve.parameters.CustomResourceDefinitionParameters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+public class KserveClient {
+
+ private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+ private final ApiClient apiClient;
+
+ private final CustomResourceDefinitionParameters crdParams;
+
+ CustomObjectsApi customObjectsApi;
+
+ @PostConstruct
+ void initialize() {
+ apiClient.setDebugging(logger.isDebugEnabled());
+ customObjectsApi = new CustomObjectsApi(apiClient);
+ }
+
+ /**
+ * Deploy the inference service.
+ *
+ * @param namespace k8s namespace
+ * @param jsonContent k8s payload
+ * @throws ApiException exception
+ */
+ public boolean deployInferenceService(String namespace, String jsonContent) throws ApiException, IOException {
+ var httpCall = customObjectsApi.createNamespacedCustomObjectCall(crdParams.getGroup(), crdParams.getVersion(),
+ namespace, crdParams.getPlural(), jsonContent.getBytes(), null, null, null, null);
+ try (Response httpResponse = httpCall.execute()) {
+ logger.debug("Response of inference service deploy in namespace {} is {}", namespace, httpResponse);
+ return httpResponse.isSuccessful();
+ }
+ }
+
+ /**
+ * Undeploy inference service.
+ *
+ * @param namespace k8s namespace
+ * @param inferenceServiceName name of the inference service
+ * @throws ApiException exception
+ */
+ public boolean undeployInferenceService(String namespace, String inferenceServiceName)
+ throws ApiException, IOException {
+ var httpCall = customObjectsApi.deleteNamespacedCustomObjectCall(crdParams.getGroup(), crdParams.getVersion(),
+ namespace, crdParams.getPlural(), inferenceServiceName, crdParams.getGracePeriod(), false, null, null,
+ null, null);
+ try (Response httpResponse = httpCall.execute()) {
+ logger.debug("Response of inference service undeploy in namespace {} is {}", namespace, httpResponse);
+ return httpResponse.isSuccessful();
+ }
+ }
+
+ /**
+ * Get the status of Inference service.
+ *
+ * @param namespace k8s namespace
+ * @param inferenceServiceName name of the inference service
+ * @return State of the inference service
+ * @throws ApiException exception on k8s client
+ * @throws IOException exception
+ */
+ public String getInferenceServiceStatus(String namespace, String inferenceServiceName)
+ throws ApiException, IOException {
+ var httpCall =
+ customObjectsApi.getNamespacedCustomObjectCall(crdParams.getGroup(), crdParams.getVersion(), namespace,
+ crdParams.getPlural(), inferenceServiceName, null);
+ try (Response httpResponse = httpCall.execute()) {
+ logger.debug("Response of getting inference service in {} is {}", namespace, httpResponse);
+ if (httpResponse.isSuccessful() && httpResponse.body() != null) {
+ JsonNode jsonNode = new ObjectMapper().readTree(httpResponse.body().string());
+ return jsonNode.at("/status/conditions/2/status").asText();
+ }
+ }
+ return Boolean.FALSE.toString();
+ }
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/models/ConfigurationEntity.java b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/models/ConfigurationEntity.java
new file mode 100755
index 000000000..ab85b7c52
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/models/ConfigurationEntity.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.models;
+
+import java.util.List;
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class ConfigurationEntity {
+
+ @NotNull
+ @Valid
+ private List<KserveInferenceEntity> kserveInferenceEntities;
+
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/models/KserveInferenceEntity.java b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/models/KserveInferenceEntity.java
new file mode 100755
index 000000000..3717970c2
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/models/KserveInferenceEntity.java
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.models;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
+import javax.validation.constraints.NotNull;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+
+@Data
+@AllArgsConstructor
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
+public class KserveInferenceEntity {
+
+ @NotNull
+ @JsonIgnore
+ private ToscaConceptIdentifier kserveInferenceEntityId;
+
+ @NotNull
+ private String name;
+
+ @NotNull
+ private String payload;
+
+ @NotNull
+ private String namespace;
+
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/parameters/CustomResourceDefinitionParameters.java b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/parameters/CustomResourceDefinitionParameters.java
new file mode 100755
index 000000000..69a7cb466
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/parameters/CustomResourceDefinitionParameters.java
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.parameters;
+
+import javax.validation.constraints.NotNull;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
+
+@Validated
+@Data
+@ConfigurationProperties(prefix = "customresourcedefinition")
+public class CustomResourceDefinitionParameters {
+
+ @NotNull
+ private String group;
+
+ @NotNull
+ private String version;
+
+ @NotNull
+ private String plural;
+
+ private int gracePeriod = 10;
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/parameters/KserveParticipantParameters.java b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/parameters/KserveParticipantParameters.java
new file mode 100755
index 000000000..81582e1f0
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/main/java/org/onap/policy/clamp/acm/participant/kserve/parameters/KserveParticipantParameters.java
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.parameters;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import lombok.Getter;
+import lombok.Setter;
+import org.onap.policy.clamp.acm.participant.intermediary.parameters.ParticipantIntermediaryParameters;
+import org.onap.policy.clamp.acm.participant.intermediary.parameters.ParticipantParameters;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
+
+/**
+ * Class to hold all parameters needed for the Kserve participant.
+ */
+@Validated
+@Getter
+@Setter
+@ConfigurationProperties(prefix = "participant")
+public class KserveParticipantParameters implements ParticipantParameters {
+
+ @NotNull
+ @Valid
+ private ParticipantIntermediaryParameters intermediaryParameters;
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/main/resources/config/application.yaml b/participant/participant-impl/participant-impl-kserve/src/main/resources/config/application.yaml
new file mode 100755
index 000000000..5ce1a7daf
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/main/resources/config/application.yaml
@@ -0,0 +1,65 @@
+spring:
+ cloud:
+ kubernetes:
+ enabled: false
+ discovery:
+ enabled: false
+ security:
+ user:
+ name: participantUser
+ password: zb!XztG34
+ autoconfigure:
+ exclude:
+ - org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
+ - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
+ - org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
+ - org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration
+ - io.kubernetes.client.spring.extended.manifests.config.KubernetesManifestsAutoConfiguration
+ - io.kubernetes.client.spring.extended.network.config.KubernetesLoadBalancerAutoConfiguration
+
+security:
+ enable-csrf: false
+
+participant:
+ intermediaryParameters:
+ reportingTimeIntervalMs: 120000
+ description: Participant Description
+ participantId: 101c62b3-8918-41b9-a747-d21eb79c6c04
+ clampAutomationCompositionTopics:
+ topicSources:
+ - topic: POLICY-ACRUNTIME-PARTICIPANT
+ servers:
+ - ${topicServer:localhost}
+ topicCommInfrastructure: dmaap
+ fetchTimeout: 15000
+ topicSinks:
+ - topic: POLICY-ACRUNTIME-PARTICIPANT
+ servers:
+ - ${topicServer:localhost}
+ topicCommInfrastructure: dmaap
+ participantSupportedElementTypes:
+ -
+ typeName: org.onap.policy.clamp.acm.KserveAutomationCompositionElement
+ typeVersion: 1.0.1
+ -
+ typeName: org.onap.policy.clamp.acm.AutomationCompositionElement
+ typeVersion: 1.0.0
+
+customresourcedefinition:
+ group: serving.kserve.io
+ version: v1beta1
+ plural: inferenceservices
+ grace-period: 10
+
+management:
+ endpoints:
+ web:
+ base-path: /
+ exposure:
+ include: health, metrics, prometheus
+server:
+ port: 8087
+ servlet:
+ context-path: /onap/policy/clamp/acm/kserveparticipant
+
+
diff --git a/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/handler/AcElementHandlerTest.java b/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/handler/AcElementHandlerTest.java
new file mode 100755
index 000000000..0743e0429
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/handler/AcElementHandlerTest.java
@@ -0,0 +1,125 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.handler;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import io.kubernetes.client.openapi.ApiException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.onap.policy.clamp.acm.participant.intermediary.api.ParticipantIntermediaryApi;
+import org.onap.policy.clamp.acm.participant.kserve.exception.KserveException;
+import org.onap.policy.clamp.acm.participant.kserve.k8s.KserveClient;
+import org.onap.policy.clamp.acm.participant.kserve.utils.CommonTestData;
+import org.onap.policy.clamp.acm.participant.kserve.utils.ToscaUtils;
+import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
+import org.onap.policy.models.base.PfModelException;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+class AcElementHandlerTest {
+
+ private final KserveClient kserveClient = mock(KserveClient.class);
+
+ @InjectMocks
+ @Spy
+ private AutomationCompositionElementHandler automationCompositionElementHandler =
+ new AutomationCompositionElementHandler(kserveClient);
+
+ @Mock
+ private ParticipantIntermediaryApi participantIntermediaryApi;
+
+ @Mock
+ private ExecutorService executor;
+ @Mock
+ private Future<String> result;
+
+ private final CommonTestData commonTestData = new CommonTestData();
+
+ private static ToscaServiceTemplate serviceTemplate;
+ private static final String KSERVE_AUTOMATION_COMPOSITION_ELEMENT =
+ "onap.policy.clamp.ac.element.KserveAutomationCompositionElement";
+
+ @BeforeAll
+ static void init() {
+ serviceTemplate = ToscaUtils.readAutomationCompositionFromTosca();
+ }
+
+ @BeforeEach
+ void startMocks() throws KserveException, ExecutionException, InterruptedException, IOException, ApiException {
+ doReturn(true).when(kserveClient).deployInferenceService(any(), any());
+ doReturn(true).when(automationCompositionElementHandler)
+ .checkInferenceServiceStatus(any(), any(), anyInt(), anyInt());
+ }
+
+ @Test
+ void test_automationCompositionElementStateChange() throws PfModelException {
+ var automationCompositionId = commonTestData.getAutomationCompositionId();
+ var element = commonTestData.getAutomationCompositionElement();
+ var automationCompositionElementId = element.getId();
+
+
+ var nodeTemplatesMap = serviceTemplate.getToscaTopologyTemplate().getNodeTemplates();
+ automationCompositionElementHandler.deploy(commonTestData.getAutomationCompositionId(), element,
+ nodeTemplatesMap.get(KSERVE_AUTOMATION_COMPOSITION_ELEMENT).getProperties());
+
+ assertDoesNotThrow(() -> automationCompositionElementHandler.undeploy(automationCompositionId,
+ automationCompositionElementId));
+
+ }
+
+ @Test
+ void test_AutomationCompositionElementUpdate() {
+ var element = commonTestData.getAutomationCompositionElement();
+
+ var nodeTemplatesMap = serviceTemplate.getToscaTopologyTemplate().getNodeTemplates();
+ assertDoesNotThrow(
+ () -> automationCompositionElementHandler.deploy(commonTestData.getAutomationCompositionId(), element,
+ nodeTemplatesMap.get(KSERVE_AUTOMATION_COMPOSITION_ELEMENT).getProperties()));
+ }
+
+ @Test
+ void test_checkInferenceServiceStatus() throws ExecutionException, InterruptedException {
+ doReturn(result).when(executor).submit(any(Runnable.class), any());
+ doReturn("Done").when(result).get();
+ doReturn(true).when(result).isDone();
+ ToscaConceptIdentifier automationCompositionId = new ToscaConceptIdentifier();
+ AutomationCompositionElement element = new AutomationCompositionElement();
+ assertDoesNotThrow(
+ () -> automationCompositionElementHandler.checkInferenceServiceStatus("sklearn-iris", "kserve-test", 1,
+ 1));
+ }
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/k8s/InferenceServiceValidatorTest.java b/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/k8s/InferenceServiceValidatorTest.java
new file mode 100755
index 000000000..3ef89a7c1
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/k8s/InferenceServiceValidatorTest.java
@@ -0,0 +1,78 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.k8s;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+
+import io.kubernetes.client.openapi.ApiException;
+import java.io.IOException;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.onap.policy.clamp.acm.participant.kserve.exception.KserveException;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+
+@ExtendWith(SpringExtension.class)
+class InferenceServiceValidatorTest {
+
+ private static int TIMEOUT = 2;
+ private static int STATUS_CHECK_INTERVAL = 1;
+
+ @MockBean
+ private KserveClient kserveClient;
+
+ String inferenceSvcName = "inference-test";
+ String namespace = "test";
+
+ @Test
+ void test_runningPodState() throws IOException, ApiException {
+ doReturn("True").when(kserveClient).getInferenceServiceStatus(any(), any());
+ var inferenceServiceValidator =
+ new InferenceServiceValidator(inferenceSvcName, namespace, TIMEOUT, STATUS_CHECK_INTERVAL,
+ kserveClient);
+ assertDoesNotThrow(inferenceServiceValidator::run);
+ }
+
+ @Test
+ void test_EmptyPodState() throws IOException, ApiException {
+ doReturn("").when(kserveClient).getInferenceServiceStatus(any(), any());
+ var inferenceServiceValidator =
+ new InferenceServiceValidator("", namespace, TIMEOUT, STATUS_CHECK_INTERVAL,
+ kserveClient);
+ assertThatThrownBy(inferenceServiceValidator::run).isInstanceOf(KserveException.class)
+ .hasMessage("Error verifying the status of the inference service. Exiting");
+ }
+
+ @Test
+ void test_PodFailureState() throws IOException, ApiException {
+ doReturn("False").when(kserveClient).getInferenceServiceStatus(any(), any());
+ var inferenceServiceValidator =
+ new InferenceServiceValidator(inferenceSvcName, namespace, TIMEOUT, STATUS_CHECK_INTERVAL,
+ kserveClient);
+ assertThatThrownBy(inferenceServiceValidator::run).isInstanceOf(KserveException.class)
+ .hasMessage("Error verifying the status of the inference service. Exiting");
+ }
+
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/k8s/KserveClientTest.java b/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/k8s/KserveClientTest.java
new file mode 100755
index 000000000..c9be352db
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/k8s/KserveClientTest.java
@@ -0,0 +1,196 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.k8s;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import io.kubernetes.client.openapi.ApiClient;
+import io.kubernetes.client.openapi.ApiException;
+import io.kubernetes.client.openapi.apis.CustomObjectsApi;
+import java.io.IOException;
+import okhttp3.Call;
+import okhttp3.MediaType;
+import okhttp3.Protocol;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+import org.apache.http.HttpStatus;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.boot.test.mock.mockito.SpyBean;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@ActiveProfiles("test")
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+class KserveClientTest {
+
+ @SpyBean
+ private KserveClient kserveClient;
+
+ String namespace = "kserve-test";
+
+ String inferenceServiceName = "sklearn-iris";
+
+ final Call remoteCall = mock(Call.class);
+
+ final CustomObjectsApi customObjectsApi = mock(CustomObjectsApi.class);
+
+ @MockBean
+ ApiClient apiClient;
+
+ @BeforeAll
+ void initialize() {
+ kserveClient.customObjectsApi = customObjectsApi;
+ }
+
+
+ @Test
+ void test_deployInferenceServiceValidResponse() throws IOException, ApiException {
+ String jsonContent =
+ "{\"apiVersion\": \"serving.kserve.io/v1beta1\",\"kind\": \"InferenceService\",\"metadata\": "
+ + "{\"name\": \"" + inferenceServiceName
+ + "\"},\"spec\": {\"predictor\": {\"model\":{\"modelFormat\": "
+ + "{\"name\": \"sklearn\"},\"storageUri\": "
+ + "\"gs://kfserving-examples/models/sklearn/1.0/model\"}}}}";
+
+ var response = getResponse(HttpStatus.SC_OK);
+ when(remoteCall.execute()).thenReturn(response);
+ when(customObjectsApi.createNamespacedCustomObjectCall(any(), any(), any(), any(), any(), any(), any(), any(),
+ any())).thenReturn(remoteCall);
+ assertTrue(kserveClient.deployInferenceService(namespace, jsonContent));
+ }
+
+ @Test
+ void test_deployInferenceServiceInvalidResponse() throws IOException, ApiException {
+ String jsonContent =
+ "{\"apiVersion\": \"serving.kserve.io/v1beta1\",\"kind\": \"InferenceService\",\"metadata\": "
+ + "{\"name\": \"" + inferenceServiceName
+ + "\"},\"spec\": {\"predictor\": {\"model\":{\"modelFormat\": "
+ + "{\"name\": \"sklearn\"},\"storageUri\": "
+ + "\"gs://kfserving-examples/models/sklearn/1.0/model\"}}}}";
+
+ var response = getResponse(HttpStatus.SC_BAD_REQUEST);
+ when(remoteCall.execute()).thenReturn(response);
+ when(customObjectsApi.createNamespacedCustomObjectCall(any(), any(), any(), any(), any(), any(), any(), any(),
+ any())).thenReturn(remoteCall);
+ assertFalse(kserveClient.deployInferenceService(namespace, jsonContent));
+ }
+
+ @Test
+ void test_deployInvalidInferenceService() throws IOException, ApiException {
+ doThrow(new ApiException("Error in deploying the service")).when(kserveClient)
+ .deployInferenceService(any(), any());
+ assertThatThrownBy(() -> kserveClient.deployInferenceService(namespace, "")).isInstanceOf(ApiException.class);
+ }
+
+ @Test
+ void test_undeployInferenceServiceValidResponse() throws IOException, ApiException {
+
+ var response = getResponse(HttpStatus.SC_OK);
+ when(remoteCall.execute()).thenReturn(response);
+ when(customObjectsApi.deleteNamespacedCustomObjectCall(any(), any(), any(), any(), any(), any(), any(), any(),
+ any(), any(), any())).thenReturn(remoteCall);
+ assertTrue(kserveClient.undeployInferenceService(namespace, inferenceServiceName));
+ }
+
+ @Test
+ void test_undeployInferenceServiceInvalidResponse() throws IOException, ApiException {
+
+ var response = getResponse(HttpStatus.SC_BAD_REQUEST);
+ when(remoteCall.execute()).thenReturn(response);
+ when(customObjectsApi.deleteNamespacedCustomObjectCall(any(), any(), any(), any(), any(), any(), any(), any(),
+ any(), any(), any())).thenReturn(remoteCall);
+ assertFalse(kserveClient.undeployInferenceService(namespace, inferenceServiceName));
+ }
+
+ @Test
+ void test_getInferenceServiceStatusValidResponse() throws IOException, ApiException {
+
+ var response = getResponse(HttpStatus.SC_OK, getInferenceServiceResponseBody("True"));
+ when(remoteCall.execute()).thenReturn(response);
+ when(customObjectsApi.getNamespacedCustomObjectCall(any(), any(), any(), any(), any(), any())).thenReturn(
+ remoteCall);
+ assertEquals("True", kserveClient.getInferenceServiceStatus(namespace, inferenceServiceName));
+ }
+
+ @Test
+ void test_getInferenceServiceStatusFalseResponse() throws IOException, ApiException {
+
+ var response = getResponse(HttpStatus.SC_OK, getInferenceServiceResponseBody("False"));
+ when(remoteCall.execute()).thenReturn(response);
+ when(customObjectsApi.getNamespacedCustomObjectCall(any(), any(), any(), any(), any(), any())).thenReturn(
+ remoteCall);
+ assertEquals("False", kserveClient.getInferenceServiceStatus(namespace, inferenceServiceName));
+ }
+
+ @Test
+ void test_getInferenceServiceStatusInvalidResponse() throws IOException, ApiException {
+
+ var response = getResponse(HttpStatus.SC_BAD_REQUEST, "");
+ when(remoteCall.execute()).thenReturn(response);
+ when(customObjectsApi.getNamespacedCustomObjectCall(any(), any(), any(), any(), any(), any())).thenReturn(
+ remoteCall);
+ assertEquals("false", kserveClient.getInferenceServiceStatus(namespace, inferenceServiceName));
+ }
+
+ Response getResponse(int code) {
+ return getResponse(code, "{}");
+ }
+
+ Response getResponse(int code, String body) {
+ return new Response.Builder().request(new Request.Builder().url("http://test").build())
+ .protocol(Protocol.HTTP_1_1).code(code).message("")
+ .body(ResponseBody.create(body, MediaType.parse("application/json"))).build();
+ }
+
+ String getInferenceServiceResponseBody(String status) {
+ return "{ \"apiVersion\": \"serving.kserve.io/v1beta1\", \"kind\": \"InferenceService\", \"spec\": "
+ + "{ \"predictor\": { \"model\": { \"modelFormat\": { \"name\": \"sklearn\" }, \"name\": \"\", "
+ + "\"resources\": {}, \"storageUri\": \"gs://kfserving-examples/models/sklearn/1.0/model\" } } "
+ + "}, \"status\": { \"address\": { \"url\": \"http://sklearn-iris.kserve-test.svc.cluster.local\""
+ + " }, \"components\": { \"predictor\": { \"latestCreatedRevision\": \"1\", \"url\": "
+ + "\"http://sklearn-iris-predictor-default-kserve-test.example.com\" } }, \"conditions\": [ "
+ + "{ \"lastTransitionTime\": \"2023-02-15T13:39:16Z\", \"status\": \"" + status
+ + "\", \"type\": "
+ + "\"IngressReady\" }, { \"lastTransitionTime\": \"2023-02-15T13:39:16Z\", \"status\": " + "\""
+ + status + "\", \"type\": \"PredictorReady\" }, { \"lastTransitionTime\": "
+ + "\"2023-02-15T13:39:16Z\", \"status\": \"" + status + "\", \"type\": \"Ready\" } ], "
+ + "\"modelStatus\": { \"copies\": { \"failedCopies\": 0, \"totalCopies\": 1 }, \"states\": "
+ + "{ \"activeModelState\": \"Loaded\", \"targetModelState\": \"Loaded\" }, \"transitionStatus\":"
+ + "\"UpToDate\" }, \"observedGeneration\": 1, \"url\": "
+ + " \"http://sklearn-iris-kserve-test.example.com\" } }";
+ }
+
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/rest/ActuatorControllerTest.java b/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/rest/ActuatorControllerTest.java
new file mode 100755
index 000000000..0cabac793
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/rest/ActuatorControllerTest.java
@@ -0,0 +1,97 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021-2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.rest;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import io.kubernetes.client.openapi.ApiClient;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.core.Response;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.onap.policy.clamp.acm.participant.kserve.utils.CommonActuatorController;
+import org.springframework.boot.test.autoconfigure.actuate.metrics.AutoConfigureMetrics;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@AutoConfigureMetrics
+@ExtendWith(SpringExtension.class)
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
+@ActiveProfiles("test")
+class ActuatorControllerTest extends CommonActuatorController {
+
+ private static final String HEALTH_ENDPOINT = "health";
+ private static final String METRICS_ENDPOINT = "metrics";
+ private static final String PROMETHEUS_ENDPOINT = "prometheus";
+
+ @MockBean
+ ApiClient apiClient;
+
+ @LocalServerPort
+ private int randomServerPort;
+
+ @BeforeEach
+ public void setUpPort() {
+ super.setHttpPrefix(randomServerPort);
+ }
+
+ @Test
+ void testGetHealth_Unauthorized() throws Exception {
+ assertUnauthorizedActGet(HEALTH_ENDPOINT);
+ }
+
+ @Test
+ void testGetMetrics_Unauthorized() throws Exception {
+ assertUnauthorizedActGet(METRICS_ENDPOINT);
+ }
+
+ @Test
+ void testGetPrometheus_Unauthorized() throws Exception {
+ assertUnauthorizedActGet(PROMETHEUS_ENDPOINT);
+ }
+
+ @Test
+ void testGetHealth() throws Exception {
+ Invocation.Builder invocationBuilder = super.sendActRequest(HEALTH_ENDPOINT);
+ Response rawresp = invocationBuilder.buildGet().invoke();
+ assertEquals(Response.Status.OK.getStatusCode(), rawresp.getStatus());
+ }
+
+ @Test
+ void testGetMetrics() throws Exception {
+ Invocation.Builder invocationBuilder = super.sendActRequest(METRICS_ENDPOINT);
+ Response rawresp = invocationBuilder.buildGet().invoke();
+ assertEquals(Response.Status.OK.getStatusCode(), rawresp.getStatus());
+ }
+
+ @Test
+ void testGePrometheus() throws Exception {
+ Invocation.Builder invocationBuilder = super.sendActRequest(PROMETHEUS_ENDPOINT);
+ Response rawresp = invocationBuilder.buildGet().invoke();
+ assertEquals(Response.Status.OK.getStatusCode(), rawresp.getStatus());
+ }
+
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/utils/CommonActuatorController.java b/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/utils/CommonActuatorController.java
new file mode 100755
index 000000000..b83a13897
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/utils/CommonActuatorController.java
@@ -0,0 +1,107 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
+import org.onap.policy.common.gson.GsonMessageBodyHandler;
+import org.onap.policy.common.utils.network.NetworkUtil;
+
+/**
+ * Class to perform Rest unit tests.
+ */
+public class CommonActuatorController {
+
+ public static final String SELF = NetworkUtil.getHostname();
+ public static final String CONTEXT_PATH = "onap/policy/clamp/acm/kserveparticipant/";
+
+ private static String httpPrefix;
+
+ /**
+ * Sends a request to an actuator endpoint.
+ *
+ * @param endpoint the target endpoint
+ * @return a request builder
+ */
+ protected Invocation.Builder sendActRequest(final String endpoint) {
+ return sendFqeRequest(httpPrefix + CONTEXT_PATH + endpoint, true);
+ }
+
+ /**
+ * Sends a request to an actuator endpoint, without any authorization header.
+ *
+ * @param endpoint the target endpoint
+ * @return a request builder
+ */
+ protected Invocation.Builder sendNoAuthActRequest(final String endpoint) {
+ return sendFqeRequest(httpPrefix + CONTEXT_PATH + endpoint, false);
+ }
+
+ /**
+ * Sends a request to a fully qualified endpoint.
+ *
+ * @param fullyQualifiedEndpoint the fully qualified target endpoint
+ * @param includeAuth if authorization header should be included
+ * @return a request builder
+ */
+ protected Invocation.Builder sendFqeRequest(final String fullyQualifiedEndpoint, boolean includeAuth) {
+ final Client client = ClientBuilder.newBuilder().build();
+
+ client.property(ClientProperties.METAINF_SERVICES_LOOKUP_DISABLE, "true");
+ client.register(GsonMessageBodyHandler.class);
+
+ if (includeAuth) {
+ client.register(HttpAuthenticationFeature.basic("participantUser", "zb!XztG34"));
+ }
+
+ final WebTarget webTarget = client.target(fullyQualifiedEndpoint);
+
+ return webTarget.request(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN);
+ }
+
+ /**
+ * Assert that GET call to actuator endpoint is Unauthorized.
+ *
+ * @param endPoint the endpoint
+ */
+ protected void assertUnauthorizedActGet(final String endPoint) {
+ Response rawresp = sendNoAuthActRequest(endPoint).buildGet().invoke();
+ assertEquals(Response.Status.UNAUTHORIZED.getStatusCode(), rawresp.getStatus());
+ }
+
+ /**
+ * Set Up httpPrefix.
+ *
+ * @param port the port
+ */
+ protected void setHttpPrefix(int port) {
+ httpPrefix = "http://" + SELF + ":" + port + "/";
+ }
+
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/utils/CommonTestData.java b/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/utils/CommonTestData.java
new file mode 100755
index 000000000..076738ac4
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/utils/CommonTestData.java
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * Modifications Copyright (C) 2022 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.utils;
+
+import java.util.List;
+import java.util.UUID;
+import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
+import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
+import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionOrderedState;
+import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+
+public class CommonTestData {
+
+ private static final String TEST_KEY_NAME = "org.onap.domain.database.KserveAutomationCompositionElement";
+
+ private static final List<UUID> AC_ID_LIST = List.of(UUID.randomUUID(), UUID.randomUUID());
+
+ /**
+ * Get a automationComposition Element.
+ *
+ * @return automationCompositionElement object
+ */
+ public AcElementDeploy getAutomationCompositionElement() {
+ var element = new AcElementDeploy();
+ element.setId(UUID.randomUUID());
+ element.setDefinition(new ToscaConceptIdentifier(TEST_KEY_NAME, "1.0.1"));
+ element.setOrderedState(DeployOrder.DEPLOY);
+ return element;
+ }
+
+ /**
+ * Get automation composition id.
+ *
+ * @return ToscaConceptIdentifier automationCompositionId
+ */
+ public UUID getAutomationCompositionId() {
+ return AC_ID_LIST.get(0);
+ }
+
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/utils/ToscaUtils.java b/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/utils/ToscaUtils.java
new file mode 100755
index 000000000..e2b00e4c9
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/test/java/org/onap/policy/clamp/acm/participant/kserve/utils/ToscaUtils.java
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.kserve.utils;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.onap.policy.common.utils.coder.YamlJsonTranslator;
+import org.onap.policy.common.utils.resources.ResourceUtils;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
+
+/**
+ * Util class for Test scope.
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class ToscaUtils {
+
+ private static final YamlJsonTranslator yamlTranslator = new YamlJsonTranslator();
+ private static final String TOSCA_TEMPLATE_YAML = "clamp/acm/test/participant-kserve.yaml";
+
+
+ /**
+ * Read a service template yaml.
+ * @return ToscaServiceTemplate
+ */
+ public static ToscaServiceTemplate readAutomationCompositionFromTosca() {
+ return serializeAutomationCompositionYaml();
+ }
+
+ private static ToscaServiceTemplate serializeAutomationCompositionYaml() {
+ String automationCompositionString = ResourceUtils.getResourceAsString(ToscaUtils.TOSCA_TEMPLATE_YAML);
+ return yamlTranslator.fromYaml(automationCompositionString, ToscaServiceTemplate.class);
+ }
+}
diff --git a/participant/participant-impl/participant-impl-kserve/src/test/resources/application-test.yaml b/participant/participant-impl/participant-impl-kserve/src/test/resources/application-test.yaml
new file mode 100755
index 000000000..8452ed071
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kserve/src/test/resources/application-test.yaml
@@ -0,0 +1,17 @@
+participant:
+ intermediaryParameters:
+ reportingTimeIntervalMs: 120000
+ description: Participant Description
+ participantId: 101c62b3-8918-41b9-a747-d21eb79c6c04
+ clampAutomationCompositionTopics:
+ topicSources:
+ - topic: POLICY-ACRUNTIME-PARTICIPANT
+ servers:
+ - ${topicServer:localhost}
+ topicCommInfrastructure: dmaap
+ fetchTimeout: 15000
+ topicSinks:
+ - topic: POLICY-ACRUNTIME-PARTICIPANT
+ servers:
+ - ${topicServer:localhost}
+ topicCommInfrastructure: dmaap \ No newline at end of file
diff --git a/participant/participant-impl/pom.xml b/participant/participant-impl/pom.xml
index aaac69f97..3df049273 100644..100755
--- a/participant/participant-impl/pom.xml
+++ b/participant/participant-impl/pom.xml
@@ -41,6 +41,7 @@
<module>participant-impl-kubernetes</module>
<module>participant-impl-http</module>
<module>participant-impl-a1pms</module>
+ <module>participant-impl-kserve</module>
</modules>
<dependencies>
diff --git a/pom.xml b/pom.xml
index c8d721c79..d11f6343d 100644..100755
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,7 @@
o policy-clamp-acm-element-impl: ACM element implementation for integration testing
o policy-clamp-participant-impl-http: A standard participant for http configurations
o policy-clamp-participant-impl-a1pms: A standard participant for accessing A1-Policy Management Service
+ o policy-clamp-participant-impl-kserve: A standard participant for Kserve
</description>
<properties>