2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2023 Nordix Foundation.
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.policy.clamp.acm.participant.kserve.k8s;
23 import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
24 import static org.junit.jupiter.api.Assertions.assertEquals;
25 import static org.junit.jupiter.api.Assertions.assertFalse;
26 import static org.junit.jupiter.api.Assertions.assertTrue;
27 import static org.mockito.ArgumentMatchers.any;
28 import static org.mockito.Mockito.doThrow;
29 import static org.mockito.Mockito.mock;
30 import static org.mockito.Mockito.when;
32 import io.kubernetes.client.openapi.ApiClient;
33 import io.kubernetes.client.openapi.ApiException;
34 import io.kubernetes.client.openapi.apis.CustomObjectsApi;
35 import java.io.IOException;
37 import okhttp3.MediaType;
38 import okhttp3.Protocol;
39 import okhttp3.Request;
40 import okhttp3.Response;
41 import okhttp3.ResponseBody;
42 import org.apache.http.HttpStatus;
43 import org.junit.jupiter.api.BeforeAll;
44 import org.junit.jupiter.api.Test;
45 import org.junit.jupiter.api.TestInstance;
46 import org.junit.jupiter.api.extension.ExtendWith;
47 import org.springframework.boot.test.context.SpringBootTest;
48 import org.springframework.boot.test.mock.mockito.MockBean;
49 import org.springframework.boot.test.mock.mockito.SpyBean;
50 import org.springframework.test.context.ActiveProfiles;
51 import org.springframework.test.context.junit.jupiter.SpringExtension;
53 @ExtendWith(SpringExtension.class)
54 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
55 @ActiveProfiles("test")
56 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
57 class KserveClientTest {
60 private KserveClient kserveClient;
62 String namespace = "kserve-test";
64 String inferenceServiceName = "sklearn-iris";
66 final Call remoteCall = mock(Call.class);
68 final CustomObjectsApi customObjectsApi = mock(CustomObjectsApi.class);
75 kserveClient.customObjectsApi = customObjectsApi;
80 void test_deployInferenceServiceValidResponse() throws IOException, ApiException {
82 "{\"apiVersion\": \"serving.kserve.io/v1beta1\",\"kind\": \"InferenceService\",\"metadata\": "
83 + "{\"name\": \"" + inferenceServiceName
84 + "\"},\"spec\": {\"predictor\": {\"model\":{\"modelFormat\": "
85 + "{\"name\": \"sklearn\"},\"storageUri\": "
86 + "\"gs://kfserving-examples/models/sklearn/1.0/model\"}}}}";
88 var response = getResponse(HttpStatus.SC_OK);
89 when(remoteCall.execute()).thenReturn(response);
90 when(customObjectsApi.createNamespacedCustomObjectCall(any(), any(), any(), any(), any(), any(), any(), any(),
91 any())).thenReturn(remoteCall);
92 assertTrue(kserveClient.deployInferenceService(namespace, jsonContent));
96 void test_deployInferenceServiceInvalidResponse() throws IOException, ApiException {
98 "{\"apiVersion\": \"serving.kserve.io/v1beta1\",\"kind\": \"InferenceService\",\"metadata\": "
99 + "{\"name\": \"" + inferenceServiceName
100 + "\"},\"spec\": {\"predictor\": {\"model\":{\"modelFormat\": "
101 + "{\"name\": \"sklearn\"},\"storageUri\": "
102 + "\"gs://kfserving-examples/models/sklearn/1.0/model\"}}}}";
104 var response = getResponse(HttpStatus.SC_BAD_REQUEST);
105 when(remoteCall.execute()).thenReturn(response);
106 when(customObjectsApi.createNamespacedCustomObjectCall(any(), any(), any(), any(), any(), any(), any(), any(),
107 any())).thenReturn(remoteCall);
108 assertFalse(kserveClient.deployInferenceService(namespace, jsonContent));
112 void test_deployInvalidInferenceService() throws IOException, ApiException {
113 doThrow(new ApiException("Error in deploying the service")).when(kserveClient)
114 .deployInferenceService(any(), any());
115 assertThatThrownBy(() -> kserveClient.deployInferenceService(namespace, "")).isInstanceOf(ApiException.class);
119 void test_undeployInferenceServiceValidResponse() throws IOException, ApiException {
121 var response = getResponse(HttpStatus.SC_OK);
122 when(remoteCall.execute()).thenReturn(response);
123 when(customObjectsApi.deleteNamespacedCustomObjectCall(any(), any(), any(), any(), any(), any(), any(), any(),
124 any(), any(), any())).thenReturn(remoteCall);
125 assertTrue(kserveClient.undeployInferenceService(namespace, inferenceServiceName));
129 void test_undeployInferenceServiceInvalidResponse() throws IOException, ApiException {
131 var response = getResponse(HttpStatus.SC_BAD_REQUEST);
132 when(remoteCall.execute()).thenReturn(response);
133 when(customObjectsApi.deleteNamespacedCustomObjectCall(any(), any(), any(), any(), any(), any(), any(), any(),
134 any(), any(), any())).thenReturn(remoteCall);
135 assertFalse(kserveClient.undeployInferenceService(namespace, inferenceServiceName));
139 void test_getInferenceServiceStatusValidResponse() throws IOException, ApiException {
141 var response = getResponse(HttpStatus.SC_OK, getInferenceServiceResponseBody("True"));
142 when(remoteCall.execute()).thenReturn(response);
143 when(customObjectsApi.getNamespacedCustomObjectCall(any(), any(), any(), any(), any(), any())).thenReturn(
145 assertEquals("True", kserveClient.getInferenceServiceStatus(namespace, inferenceServiceName));
149 void test_getInferenceServiceStatusFalseResponse() throws IOException, ApiException {
151 var response = getResponse(HttpStatus.SC_OK, getInferenceServiceResponseBody("False"));
152 when(remoteCall.execute()).thenReturn(response);
153 when(customObjectsApi.getNamespacedCustomObjectCall(any(), any(), any(), any(), any(), any())).thenReturn(
155 assertEquals("False", kserveClient.getInferenceServiceStatus(namespace, inferenceServiceName));
159 void test_getInferenceServiceStatusInvalidResponse() throws IOException, ApiException {
161 var response = getResponse(HttpStatus.SC_BAD_REQUEST, "");
162 when(remoteCall.execute()).thenReturn(response);
163 when(customObjectsApi.getNamespacedCustomObjectCall(any(), any(), any(), any(), any(), any())).thenReturn(
165 assertEquals("false", kserveClient.getInferenceServiceStatus(namespace, inferenceServiceName));
168 Response getResponse(int code) {
169 return getResponse(code, "{}");
172 Response getResponse(int code, String body) {
173 return new Response.Builder().request(new Request.Builder().url("http://test").build())
174 .protocol(Protocol.HTTP_1_1).code(code).message("")
175 .body(ResponseBody.create(body, MediaType.parse("application/json"))).build();
178 String getInferenceServiceResponseBody(String status) {
179 return "{ \"apiVersion\": \"serving.kserve.io/v1beta1\", \"kind\": \"InferenceService\", \"spec\": "
180 + "{ \"predictor\": { \"model\": { \"modelFormat\": { \"name\": \"sklearn\" }, \"name\": \"\", "
181 + "\"resources\": {}, \"storageUri\": \"gs://kfserving-examples/models/sklearn/1.0/model\" } } "
182 + "}, \"status\": { \"address\": { \"url\": \"http://sklearn-iris.kserve-test.svc.cluster.local\""
183 + " }, \"components\": { \"predictor\": { \"latestCreatedRevision\": \"1\", \"url\": "
184 + "\"http://sklearn-iris-predictor-default-kserve-test.example.com\" } }, \"conditions\": [ "
185 + "{ \"lastTransitionTime\": \"2023-02-15T13:39:16Z\", \"status\": \"" + status
187 + "\"IngressReady\" }, { \"lastTransitionTime\": \"2023-02-15T13:39:16Z\", \"status\": " + "\""
188 + status + "\", \"type\": \"PredictorReady\" }, { \"lastTransitionTime\": "
189 + "\"2023-02-15T13:39:16Z\", \"status\": \"" + status + "\", \"type\": \"Ready\" } ], "
190 + "\"modelStatus\": { \"copies\": { \"failedCopies\": 0, \"totalCopies\": 1 }, \"states\": "
191 + "{ \"activeModelState\": \"Loaded\", \"targetModelState\": \"Loaded\" }, \"transitionStatus\":"
192 + "\"UpToDate\" }, \"observedGeneration\": 1, \"url\": "
193 + " \"http://sklearn-iris-kserve-test.example.com\" } }";