c9be352db11a3ac6dd5f19aa2fe65a115142f7f2
[policy/clamp.git] /
1 /*-
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
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.clamp.acm.participant.kserve.k8s;
22
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;
31
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;
36 import okhttp3.Call;
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;
52
53 @ExtendWith(SpringExtension.class)
54 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
55 @ActiveProfiles("test")
56 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
57 class KserveClientTest {
58
59     @SpyBean
60     private KserveClient kserveClient;
61
62     String namespace = "kserve-test";
63
64     String inferenceServiceName = "sklearn-iris";
65
66     final Call remoteCall = mock(Call.class);
67
68     final CustomObjectsApi customObjectsApi = mock(CustomObjectsApi.class);
69
70     @MockBean
71     ApiClient apiClient;
72
73     @BeforeAll
74     void initialize() {
75         kserveClient.customObjectsApi = customObjectsApi;
76     }
77
78
79     @Test
80     void test_deployInferenceServiceValidResponse() throws IOException, ApiException {
81         String jsonContent =
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\"}}}}";
87
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));
93     }
94
95     @Test
96     void test_deployInferenceServiceInvalidResponse() throws IOException, ApiException {
97         String jsonContent =
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\"}}}}";
103
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));
109     }
110
111     @Test
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);
116     }
117
118     @Test
119     void test_undeployInferenceServiceValidResponse() throws IOException, ApiException {
120
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));
126     }
127
128     @Test
129     void test_undeployInferenceServiceInvalidResponse() throws IOException, ApiException {
130
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));
136     }
137
138     @Test
139     void test_getInferenceServiceStatusValidResponse() throws IOException, ApiException {
140
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(
144                 remoteCall);
145         assertEquals("True", kserveClient.getInferenceServiceStatus(namespace, inferenceServiceName));
146     }
147
148     @Test
149     void test_getInferenceServiceStatusFalseResponse() throws IOException, ApiException {
150
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(
154                 remoteCall);
155         assertEquals("False", kserveClient.getInferenceServiceStatus(namespace, inferenceServiceName));
156     }
157
158     @Test
159     void test_getInferenceServiceStatusInvalidResponse() throws IOException, ApiException {
160
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(
164                 remoteCall);
165         assertEquals("false", kserveClient.getInferenceServiceStatus(namespace, inferenceServiceName));
166     }
167
168     Response getResponse(int code) {
169         return getResponse(code, "{}");
170     }
171
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();
176     }
177
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
186                        + "\", \"type\": "
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\" } }";
194     }
195
196 }