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.so.cnfm.lcm.bpmn.flows.extclients.kubernetes;
23 import static org.onap.so.cnfm.lcm.bpmn.flows.Constants.KIND_DAEMON_SET;
24 import static org.onap.so.cnfm.lcm.bpmn.flows.Constants.KIND_DEPLOYMENT;
25 import static org.onap.so.cnfm.lcm.bpmn.flows.Constants.KIND_JOB;
26 import static org.onap.so.cnfm.lcm.bpmn.flows.Constants.KIND_POD;
27 import static org.onap.so.cnfm.lcm.bpmn.flows.Constants.KIND_REPLICA_SET;
28 import static org.onap.so.cnfm.lcm.bpmn.flows.Constants.KIND_SERVICE;
29 import static org.onap.so.cnfm.lcm.bpmn.flows.Constants.KIND_STATEFUL_SET;
30 import java.io.IOException;
31 import java.lang.reflect.Type;
32 import java.net.SocketTimeoutException;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.List;
38 import java.util.Map.Entry;
39 import java.util.Optional;
40 import java.util.function.Predicate;
41 import java.util.stream.Collectors;
42 import org.onap.so.cnfm.lcm.bpmn.flows.exceptions.KubernetesRequestProcessingException;
43 import org.onap.so.cnfm.lcm.bpmn.flows.exceptions.KubernetesRequestTimeOut;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46 import org.springframework.beans.factory.annotation.Value;
47 import org.springframework.stereotype.Service;
48 import com.google.gson.reflect.TypeToken;
49 import io.kubernetes.client.apimachinery.GroupVersion;
50 import io.kubernetes.client.common.KubernetesListObject;
51 import io.kubernetes.client.common.KubernetesObject;
52 import io.kubernetes.client.custom.IntOrString;
53 import io.kubernetes.client.openapi.ApiClient;
54 import io.kubernetes.client.openapi.ApiException;
55 import io.kubernetes.client.openapi.apis.AppsV1Api;
56 import io.kubernetes.client.openapi.apis.BatchV1Api;
57 import io.kubernetes.client.openapi.apis.CoreV1Api;
58 import io.kubernetes.client.openapi.models.V1DaemonSet;
59 import io.kubernetes.client.openapi.models.V1DaemonSetList;
60 import io.kubernetes.client.openapi.models.V1DaemonSetSpec;
61 import io.kubernetes.client.openapi.models.V1DaemonSetStatus;
62 import io.kubernetes.client.openapi.models.V1Deployment;
63 import io.kubernetes.client.openapi.models.V1DeploymentList;
64 import io.kubernetes.client.openapi.models.V1DeploymentSpec;
65 import io.kubernetes.client.openapi.models.V1DeploymentStatus;
66 import io.kubernetes.client.openapi.models.V1Job;
67 import io.kubernetes.client.openapi.models.V1JobCondition;
68 import io.kubernetes.client.openapi.models.V1JobList;
69 import io.kubernetes.client.openapi.models.V1ObjectMeta;
70 import io.kubernetes.client.openapi.models.V1Pod;
71 import io.kubernetes.client.openapi.models.V1PodCondition;
72 import io.kubernetes.client.openapi.models.V1PodList;
73 import io.kubernetes.client.openapi.models.V1ReplicaSet;
74 import io.kubernetes.client.openapi.models.V1ReplicaSetList;
75 import io.kubernetes.client.openapi.models.V1ReplicaSetSpec;
76 import io.kubernetes.client.openapi.models.V1ReplicaSetStatus;
77 import io.kubernetes.client.openapi.models.V1RollingUpdateStatefulSetStrategy;
78 import io.kubernetes.client.openapi.models.V1Service;
79 import io.kubernetes.client.openapi.models.V1ServiceList;
80 import io.kubernetes.client.openapi.models.V1StatefulSet;
81 import io.kubernetes.client.openapi.models.V1StatefulSetList;
82 import io.kubernetes.client.openapi.models.V1StatefulSetSpec;
83 import io.kubernetes.client.openapi.models.V1StatefulSetStatus;
84 import io.kubernetes.client.openapi.models.V1StatefulSetUpdateStrategy;
85 import io.kubernetes.client.util.Watch;
86 import io.kubernetes.client.util.Watch.Response;
91 * @author Waqas Ikram (waqas.ikram@est.tech)
95 public class KubernetesClientImpl implements KubernetesClient {
96 private static final String ROLLING_UPDATE = "RollingUpdate";
97 private static final String EVENT_TYPE_ERROR = "ERROR";
98 private static final String EVENT_TYPE_DELETED = "DELETED";
99 private static final String EVENT_TYPE_MODIFIED = "MODIFIED";
100 private static final String EVENT_TYPE_ADDED = "ADDED";
101 private static final String TRUE_STRING = Boolean.TRUE.toString();
102 private static final String JOB_FAILED = "Failed";
103 private static final String JOB_COMPLETE = "Complete";
104 private static final boolean DISABLE_WATCH = false;
105 private static final boolean ENABLE_WATCH = true;
106 private static final String POD_READY = "Ready";
108 private static final Logger logger = LoggerFactory.getLogger(KubernetesClientImpl.class);
111 * Event Listener timeout in seconds Note: this should be less then the timeout camunda task execution
113 @Value("${kubernetes.client.http-request.timeoutSeconds:5}")
114 private Integer timeoutSeconds;
117 public boolean isJobReady(final ApiClient apiClient, final String labelSelector)
118 throws KubernetesRequestProcessingException {
119 logger.debug("Will check if Job is ready using labelSelector: {}", labelSelector);
121 final BatchV1Api batchV1Api = new BatchV1Api(apiClient);
122 final Call call = batchV1Api.listJobForAllNamespacesCall(null, null, null, labelSelector, null, null, null,
123 null, timeoutSeconds, ENABLE_WATCH, null);
125 final Map<V1Job, String> readyResources =
126 getReadyResources(apiClient, call, new TypeToken<Response<V1Job>>() {}.getType());
128 if (!readyResources.isEmpty()) {
129 final List<Entry<V1Job, String>> jobNotReadyList = readyResources.entrySet().stream()
130 .filter(entry -> !isResourceReady(entry.getKey(), entry.getValue(), this::isJobReady))
131 .collect(Collectors.toList());
133 if (jobNotReadyList.isEmpty()) {
134 logger.debug("JobList is ready ...");
137 logger.debug("JobList is not yet Ready: {}", jobNotReadyList);
142 logger.warn("No items found in jobList : {}", readyResources);
145 } catch (final ApiException exception) {
146 handleApiException(KIND_JOB, labelSelector, exception);
147 } catch (final RuntimeException runtimeException) {
148 handleRuntimeException(KIND_JOB, labelSelector, runtimeException);
150 logger.debug("Returning false as Job is not ready ...");
155 public boolean isPodReady(final ApiClient apiClient, final String labelSelector)
156 throws KubernetesRequestProcessingException {
157 logger.debug("Will check if Pod is ready using labelSelector: {}", labelSelector);
159 final CoreV1Api coreV1Api = new CoreV1Api(apiClient);
160 final Call call = coreV1Api.listPodForAllNamespacesCall(null, null, null, labelSelector, null, null, null,
161 null, timeoutSeconds, ENABLE_WATCH, null);
163 final Map<V1Pod, String> readyResources =
164 getReadyResources(apiClient, call, new TypeToken<Response<V1Pod>>() {}.getType());
166 if (!readyResources.isEmpty()) {
167 final List<Entry<V1Pod, String>> podNotReadyList = readyResources.entrySet().stream()
168 .filter(entry -> !isResourceReady(entry.getKey(), entry.getValue(), this::isPodReady))
169 .collect(Collectors.toList());
171 if (podNotReadyList.isEmpty()) {
172 logger.debug("PodList is ready ...");
175 logger.debug("PodList is not yet Ready: {}", podNotReadyList);
180 logger.warn("No items found in podList : {}", readyResources);
183 } catch (final ApiException exception) {
184 handleApiException(KIND_POD, labelSelector, exception);
185 } catch (final RuntimeException runtimeException) {
186 handleRuntimeException(KIND_POD, labelSelector, runtimeException);
189 logger.debug("Returning false as Pod is not ready ...");
195 public boolean isServiceReady(final ApiClient apiClient, final String labelSelector)
196 throws KubernetesRequestProcessingException {
197 logger.debug("Will check if Service is ready using labelSelector: {}", labelSelector);
199 final CoreV1Api api = new CoreV1Api(apiClient);
200 final Call call = api.listServiceForAllNamespacesCall(null, null, null, labelSelector, null, null, null,
201 null, timeoutSeconds, ENABLE_WATCH, null);
203 final Map<V1Service, String> readyResources =
204 getReadyResources(apiClient, call, new TypeToken<Response<V1Service>>() {}.getType());
206 if (!readyResources.isEmpty()) {
207 final List<Entry<V1Service, String>> serviceNotReadyList = readyResources.entrySet().stream()
208 .filter(entry -> !isResourceReady(entry.getKey(), entry.getValue(), this::isServiceReady))
209 .collect(Collectors.toList());
211 if (serviceNotReadyList.isEmpty()) {
212 logger.debug("ServiceList is ready ...");
215 logger.debug("ServiceList is not yet Ready: {}", serviceNotReadyList);
220 logger.warn("No items found in serviceList : {}", readyResources);
223 } catch (final ApiException exception) {
224 handleApiException(KIND_SERVICE, labelSelector, exception);
225 } catch (final RuntimeException runtimeException) {
226 handleRuntimeException(KIND_SERVICE, labelSelector, runtimeException);
229 logger.debug("Returning false as Service is not ready ...");
234 public boolean isDeploymentReady(final ApiClient apiClient, final String labelSelector)
235 throws KubernetesRequestProcessingException {
236 logger.debug("Will check if Deployment is ready using labelSelector: {}", labelSelector);
239 final AppsV1Api appsV1Api = new AppsV1Api(apiClient);
240 final Call call = appsV1Api.listDeploymentForAllNamespacesCall(null, null, null, labelSelector, null, null,
241 null, null, timeoutSeconds, ENABLE_WATCH, null);
243 final Map<V1Deployment, String> readyResources =
244 getReadyResources(apiClient, call, new TypeToken<Response<V1Deployment>>() {}.getType());
246 if (!readyResources.isEmpty()) {
247 final List<Entry<V1Deployment, String>> deploymentNotReadyList = readyResources.entrySet().stream()
248 .filter(entry -> !isResourceReady(entry.getKey(), entry.getValue(), this::isDeploymentReady))
249 .collect(Collectors.toList());
251 if (deploymentNotReadyList.isEmpty()) {
252 logger.debug("DeploymentList is ready ...");
255 logger.debug("DeploymentList is not yet Ready: {}", deploymentNotReadyList);
260 logger.warn("No items found in deploymentList : {}", readyResources);
263 } catch (final ApiException exception) {
264 handleApiException(KIND_DEPLOYMENT, labelSelector, exception);
265 } catch (final RuntimeException runtimeException) {
266 handleRuntimeException(KIND_DEPLOYMENT, labelSelector, runtimeException);
269 logger.debug("Returning false as Deployment is not ready ...");
274 public boolean isReplicaSetReady(final ApiClient apiClient, final String labelSelector)
275 throws KubernetesRequestProcessingException {
276 logger.debug("Will check if ReplicaSet is ready using labelSelector: {}", labelSelector);
278 final AppsV1Api appsV1Api = new AppsV1Api(apiClient);
279 final Call call = appsV1Api.listReplicaSetForAllNamespacesCall(null, null, null, labelSelector, null, null,
280 null, null, timeoutSeconds, ENABLE_WATCH, null);
282 final Map<V1ReplicaSet, String> readyResources =
283 getReadyResources(apiClient, call, new TypeToken<Response<V1ReplicaSet>>() {}.getType());
285 if (!readyResources.isEmpty()) {
286 final List<Entry<V1ReplicaSet, String>> replicaSet = readyResources.entrySet().stream()
287 .filter(entry -> !isResourceReady(entry.getKey(), entry.getValue(), this::isReplicaSetReady))
288 .collect(Collectors.toList());
290 if (replicaSet.isEmpty()) {
291 logger.debug("ReplicaSetList is ready ...");
294 logger.debug("ReplicaSetList is not yet Ready: {}", replicaSet);
299 logger.warn("No items found in replicaSetList : {}", readyResources);
302 } catch (final ApiException exception) {
303 handleApiException(KIND_REPLICA_SET, labelSelector, exception);
304 } catch (final RuntimeException runtimeException) {
305 handleRuntimeException(KIND_REPLICA_SET, labelSelector, runtimeException);
307 logger.debug("Returning false as ReplicaSet is not ready ...");
312 public boolean isDaemonSetReady(final ApiClient apiClient, final String labelSelector)
313 throws KubernetesRequestProcessingException {
314 logger.debug("Will check if DaemonSet is ready using labelSelector: {}", labelSelector);
316 final AppsV1Api appsV1Api = new AppsV1Api(apiClient);
317 final Call call = appsV1Api.listDaemonSetForAllNamespacesCall(null, null, null, labelSelector, null, null,
318 null, null, timeoutSeconds, ENABLE_WATCH, null);
320 final Map<V1DaemonSet, String> readyResources =
321 getReadyResources(apiClient, call, new TypeToken<Response<V1DaemonSet>>() {}.getType());
323 if (!readyResources.isEmpty()) {
324 final List<Entry<V1DaemonSet, String>> daemonSetNotReadyList = readyResources.entrySet().stream()
325 .filter(entry -> !isResourceReady(entry.getKey(), entry.getValue(), this::isDaemonSetReady))
326 .collect(Collectors.toList());
328 if (daemonSetNotReadyList.isEmpty()) {
329 logger.debug("DaemonSetList is ready ...");
332 logger.debug("DaemonSetList is not yet Ready: {}", daemonSetNotReadyList);
337 logger.warn("No items found in daemonSetList : {}", readyResources);
340 } catch (final ApiException exception) {
341 handleApiException(KIND_DAEMON_SET, labelSelector, exception);
342 } catch (final RuntimeException runtimeException) {
343 handleRuntimeException(KIND_DAEMON_SET, labelSelector, runtimeException);
345 logger.debug("Returning false as DaemonSet is not ready ...");
350 public boolean isStatefulSetReady(final ApiClient apiClient, final String labelSelector)
351 throws KubernetesRequestProcessingException {
352 logger.debug("Will check if StatefulSet is ready using labelSelector: {}", labelSelector);
354 final AppsV1Api appsV1Api = new AppsV1Api(apiClient);
355 final Call call = appsV1Api.listStatefulSetForAllNamespacesCall(null, null, null, labelSelector, null, null,
356 null, null, timeoutSeconds, ENABLE_WATCH, null);
358 final Map<V1StatefulSet, String> readyResources =
359 getReadyResources(apiClient, call, new TypeToken<Response<V1StatefulSet>>() {}.getType());
361 if (!readyResources.isEmpty()) {
362 final List<Entry<V1StatefulSet, String>> statefulSetNotReadyList = readyResources.entrySet().stream()
363 .filter(entry -> !isResourceReady(entry.getKey(), entry.getValue(), this::isStatefulSetReady))
364 .collect(Collectors.toList());
366 if (statefulSetNotReadyList.isEmpty()) {
367 logger.debug("StatefulSetList is ready ...");
370 logger.debug("StatefulSetList is not yet Ready: {}", statefulSetNotReadyList);
375 logger.warn("No items found in statefulSetList : {}", readyResources);
378 } catch (final ApiException exception) {
379 handleApiException(KIND_STATEFUL_SET, labelSelector, exception);
380 } catch (final RuntimeException runtimeException) {
381 handleRuntimeException(KIND_STATEFUL_SET, labelSelector, runtimeException);
383 logger.debug("Returning false as StatefulSet is not ready ...");
388 public boolean isServiceDeleted(final ApiClient apiClient, final String labelSelector)
389 throws KubernetesRequestProcessingException {
390 logger.debug("Check is Service deleted by using labelSelector: {}", labelSelector);
392 final CoreV1Api coreV1Api = new CoreV1Api(apiClient);
393 final V1ServiceList v1ServiceList = coreV1Api.listServiceForAllNamespaces(null, null, null, labelSelector,
394 null, null, null, null, timeoutSeconds, DISABLE_WATCH);
395 logger.debug("Response from list service for all Namespaces: {}", v1ServiceList);
396 return v1ServiceList.getItems().isEmpty();
397 } catch (final ApiException exception) {
398 logger.debug("Return false because of exception occurred: {}", exception.getMessage());
399 handleApiException(KIND_SERVICE, labelSelector, exception);
400 } catch (final RuntimeException runtimeException) {
401 logger.debug("Return false because of Runtime exception occurred: {}", runtimeException.getMessage());
402 handleRuntimeException(KIND_SERVICE, labelSelector, runtimeException);
404 logger.debug("Returning false as Service is not Deleted ...");
409 public boolean isPodDeleted(final ApiClient apiClient, final String labelSelector)
410 throws KubernetesRequestProcessingException {
411 logger.debug("Check is Pod deleted by using labelSelector: {}", labelSelector);
413 final CoreV1Api coreV1Api = new CoreV1Api(apiClient);
414 final V1PodList v1PodList = coreV1Api.listPodForAllNamespaces(null, null, null, labelSelector, null, null,
415 null, null, timeoutSeconds, DISABLE_WATCH);
416 logger.debug("Response from list Pod for all Namespaces: {}", v1PodList);
417 return v1PodList.getItems().isEmpty();
418 } catch (final ApiException exception) {
419 logger.debug("Return false because of exception occurred: {}", exception.getMessage());
420 handleApiException(KIND_POD, labelSelector, exception);
421 } catch (final RuntimeException runtimeException) {
422 logger.debug("Return false because of Runtime exception occurred: {}", runtimeException.getMessage());
423 handleRuntimeException(KIND_POD, labelSelector, runtimeException);
425 logger.debug("Returning false as Pod is not Deleted ...");
430 public boolean isJobDeleted(final ApiClient apiClient, final String labelSelector)
431 throws KubernetesRequestProcessingException {
432 logger.debug("Check is Job deleted by using labelSelector: {}", labelSelector);
434 final BatchV1Api batchV1Api = new BatchV1Api(apiClient);
435 final V1JobList v1JobList = batchV1Api.listJobForAllNamespaces(null, null, null, labelSelector, null, null,
436 null, null, timeoutSeconds, DISABLE_WATCH);
437 logger.debug("Response from list Job for all Namespaces: {}", v1JobList);
438 return v1JobList.getItems().isEmpty();
439 } catch (final ApiException exception) {
440 logger.debug("Return false because of exception occurred: {}", exception.getMessage());
441 handleApiException(KIND_JOB, labelSelector, exception);
442 } catch (final RuntimeException runtimeException) {
443 logger.debug("Return false because of Runtime exception occurred: {}", runtimeException.getMessage());
444 handleRuntimeException(KIND_JOB, labelSelector, runtimeException);
446 logger.debug("Returning false as Job is not Deleted ...");
451 public boolean isDeploymentDeleted(final ApiClient apiClient, final String labelSelector)
452 throws KubernetesRequestProcessingException {
453 logger.debug("Check is Deployment deleted by using labelSelector: {}", labelSelector);
455 final AppsV1Api batchV1Api = new AppsV1Api(apiClient);
456 final V1DeploymentList v1DeploymentList = batchV1Api.listDeploymentForAllNamespaces(null, null, null,
457 labelSelector, null, null, null, null, timeoutSeconds, DISABLE_WATCH);
458 logger.debug("Response from list Deployment for all Namespaces: {}", v1DeploymentList);
459 return v1DeploymentList.getItems().isEmpty();
460 } catch (final ApiException exception) {
461 logger.debug("Return false because of exception occurred: {}", exception.getMessage());
462 handleApiException(KIND_DEPLOYMENT, labelSelector, exception);
463 } catch (final RuntimeException runtimeException) {
464 logger.debug("Return false because of Runtime exception occurred: {}", runtimeException.getMessage());
465 handleRuntimeException(KIND_DEPLOYMENT, labelSelector, runtimeException);
467 logger.debug("Returning false as Deployment is not Deleted ...");
472 public boolean isReplicaSetDeleted(final ApiClient apiClient, final String labelSelector)
473 throws KubernetesRequestProcessingException {
474 logger.debug("Check is ReplicaSet deleted by using labelSelector: {}", labelSelector);
476 final AppsV1Api batchV1Api = new AppsV1Api(apiClient);
477 final V1ReplicaSetList v1ReplicaSetList = batchV1Api.listReplicaSetForAllNamespaces(null, null, null,
478 labelSelector, null, null, null, null, timeoutSeconds, DISABLE_WATCH);
479 logger.debug("Response from list ReplicaSet for all Namespaces: {}", v1ReplicaSetList);
480 return v1ReplicaSetList.getItems().isEmpty();
481 } catch (final ApiException exception) {
482 logger.debug("Return false because of exception occurred: {}", exception.getMessage());
483 handleApiException(KIND_REPLICA_SET, labelSelector, exception);
484 } catch (final RuntimeException runtimeException) {
485 logger.debug("Return false because of Runtime exception occurred: {}", runtimeException.getMessage());
486 handleRuntimeException(KIND_REPLICA_SET, labelSelector, runtimeException);
488 logger.debug("Returning false as ReplicaSet is not Deleted ...");
493 public boolean isDaemonSetDeleted(final ApiClient apiClient, final String labelSelector)
494 throws KubernetesRequestProcessingException {
495 logger.debug("Check is DaemonSet deleted by using labelSelector: {}", labelSelector);
497 final AppsV1Api batchV1Api = new AppsV1Api(apiClient);
498 final V1DaemonSetList v1DaemonSetList = batchV1Api.listDaemonSetForAllNamespaces(null, null, null,
499 labelSelector, null, null, null, null, timeoutSeconds, DISABLE_WATCH);
500 logger.debug("Response from list DaemonSet for all Namespaces: {}", v1DaemonSetList);
501 return v1DaemonSetList.getItems().isEmpty();
502 } catch (final ApiException exception) {
503 logger.debug("Return false because of exception occurred: {}", exception.getMessage());
504 handleApiException(KIND_DAEMON_SET, labelSelector, exception);
505 } catch (final RuntimeException runtimeException) {
506 logger.debug("Return false because of Runtime exception occurred: {}", runtimeException.getMessage());
507 handleRuntimeException(KIND_DAEMON_SET, labelSelector, runtimeException);
509 logger.debug("Returning false as DaemonSet is not Deleted ...");
514 public boolean isStatefulSetDeleted(final ApiClient apiClient, final String labelSelector)
515 throws KubernetesRequestProcessingException {
516 logger.debug("Check is StatefulSet deleted by using labelSelector: {}", labelSelector);
518 final AppsV1Api batchV1Api = new AppsV1Api(apiClient);
519 final V1StatefulSetList v1StatefulSetList = batchV1Api.listStatefulSetForAllNamespaces(null, null, null,
520 labelSelector, null, null, null, null, timeoutSeconds, DISABLE_WATCH);
521 logger.debug("Response from list StatefulSet for all Namespaces: {}", v1StatefulSetList);
522 return v1StatefulSetList.getItems().isEmpty();
523 } catch (final ApiException exception) {
524 logger.debug("Return false because of exception occurred: {}", exception.getMessage());
525 handleApiException(KIND_STATEFUL_SET, labelSelector, exception);
526 } catch (final RuntimeException runtimeException) {
527 logger.debug("Return false because of Runtime exception occurred: {}", runtimeException.getMessage());
528 handleRuntimeException(KIND_STATEFUL_SET, labelSelector, runtimeException);
530 logger.debug("Returning false as StatefulSet is not Deleted ...");
536 public List<KubernetesResource> getJobResources(final ApiClient apiClient, final String labelSelector)
537 throws KubernetesRequestProcessingException {
538 logger.debug("Retrieving Jobs using labelSelector: {}", labelSelector);
540 final BatchV1Api batchV1Api = new BatchV1Api(apiClient);
541 final V1JobList jobList = batchV1Api.listJobForAllNamespaces(null, null, null, labelSelector, null, null,
542 null, null, timeoutSeconds, DISABLE_WATCH);
544 logger.debug("Received Jobs: {}", jobList);
545 return getKubernetesResource(jobList);
547 } catch (final ApiException exception) {
548 handleApiException(KIND_JOB, labelSelector, exception);
549 } catch (final IllegalArgumentException illegalArgumentException) {
550 handleIllegalArgumentException(KIND_JOB, labelSelector, illegalArgumentException);
551 } catch (final RuntimeException runtimeException) {
552 handleRuntimeException(KIND_JOB, labelSelector, runtimeException);
555 logger.error("Unable to find any job resources ...");
556 return Collections.emptyList();
560 public List<KubernetesResource> getDeploymentResources(final ApiClient apiClient, final String labelSelector)
561 throws KubernetesRequestProcessingException {
562 logger.debug("Retrieving Deployment using labelSelector: {}", labelSelector);
564 final AppsV1Api appsV1Api = new AppsV1Api(apiClient);
565 final V1DeploymentList deploymentList = appsV1Api.listDeploymentForAllNamespaces(null, null, null,
566 labelSelector, null, null, null, null, timeoutSeconds, DISABLE_WATCH);
568 logger.debug("Received Deployments: {}", deploymentList);
569 return getKubernetesResource(deploymentList);
571 } catch (final ApiException exception) {
572 handleApiException(KIND_DEPLOYMENT, labelSelector, exception);
573 } catch (final IllegalArgumentException illegalArgumentException) {
574 handleIllegalArgumentException(KIND_DEPLOYMENT, labelSelector, illegalArgumentException);
575 } catch (final RuntimeException runtimeException) {
576 handleRuntimeException(KIND_DEPLOYMENT, labelSelector, runtimeException);
579 logger.error("Unable to find any Deployment resources ...");
580 return Collections.emptyList();
584 public List<KubernetesResource> getPodResources(final ApiClient apiClient, final String labelSelector)
585 throws KubernetesRequestProcessingException {
586 logger.debug("Retrieving Pod using labelSelector: {}", labelSelector);
588 final CoreV1Api coreV1Api = new CoreV1Api(apiClient);
589 final V1PodList podList = coreV1Api.listPodForAllNamespaces(null, null, null, labelSelector, null, null,
590 null, null, timeoutSeconds, DISABLE_WATCH);
592 logger.debug("Received Pods: {}", podList);
593 return getKubernetesResource(podList);
595 } catch (final ApiException exception) {
596 handleApiException(KIND_POD, labelSelector, exception);
597 } catch (final IllegalArgumentException illegalArgumentException) {
598 handleIllegalArgumentException(KIND_POD, labelSelector, illegalArgumentException);
599 } catch (final RuntimeException runtimeException) {
600 handleRuntimeException(KIND_POD, labelSelector, runtimeException);
603 logger.error("Unable to find any Pod resources ...");
604 return Collections.emptyList();
608 public List<KubernetesResource> getServiceResources(final ApiClient apiClient, final String labelSelector)
609 throws KubernetesRequestProcessingException {
610 logger.debug("Retrieving Service using labelSelector: {}", labelSelector);
612 final CoreV1Api coreV1Api = new CoreV1Api(apiClient);
613 final V1ServiceList serviceList = coreV1Api.listServiceForAllNamespaces(null, null, null, labelSelector,
614 null, null, null, null, timeoutSeconds, DISABLE_WATCH);
616 logger.debug("Received Services: {}", serviceList);
617 return getKubernetesResource(serviceList);
619 } catch (final ApiException exception) {
620 handleApiException(KIND_SERVICE, labelSelector, exception);
621 } catch (final IllegalArgumentException illegalArgumentException) {
622 handleIllegalArgumentException(KIND_SERVICE, labelSelector, illegalArgumentException);
623 } catch (final RuntimeException runtimeException) {
624 handleRuntimeException(KIND_SERVICE, labelSelector, runtimeException);
627 logger.error("Unable to find any Service resources ...");
628 return Collections.emptyList();
632 public List<KubernetesResource> getReplicaSetResources(final ApiClient apiClient, final String labelSelector)
633 throws KubernetesRequestProcessingException {
634 logger.debug("Retrieving ReplicaSet using labelSelector: {}", labelSelector);
636 final AppsV1Api appsV1Api = new AppsV1Api(apiClient);
637 final V1ReplicaSetList replicaSetList = appsV1Api.listReplicaSetForAllNamespaces(null, null, null,
638 labelSelector, null, null, null, null, timeoutSeconds, DISABLE_WATCH);
640 logger.debug("Received ReplicaSets: {}", replicaSetList);
641 return getKubernetesResource(replicaSetList);
643 } catch (final ApiException exception) {
644 handleApiException(KIND_REPLICA_SET, labelSelector, exception);
645 } catch (final IllegalArgumentException illegalArgumentException) {
646 handleIllegalArgumentException(KIND_REPLICA_SET, labelSelector, illegalArgumentException);
647 } catch (final RuntimeException runtimeException) {
648 handleRuntimeException(KIND_REPLICA_SET, labelSelector, runtimeException);
651 logger.error("Unable to find any ReplicaSet resources ...");
652 return Collections.emptyList();
656 public List<KubernetesResource> getDaemonSetResources(final ApiClient apiClient, final String labelSelector)
657 throws KubernetesRequestProcessingException {
658 logger.debug("Retrieving DaemonSet using labelSelector: {}", labelSelector);
660 final AppsV1Api appsV1Api = new AppsV1Api(apiClient);
662 final V1DaemonSetList daemonSetList = appsV1Api.listDaemonSetForAllNamespaces(null, null, null,
663 labelSelector, null, null, null, null, timeoutSeconds, DISABLE_WATCH);
665 logger.debug("Received DaemonSets: {}", daemonSetList);
666 return getKubernetesResource(daemonSetList);
668 } catch (final ApiException exception) {
669 handleApiException(KIND_DAEMON_SET, labelSelector, exception);
670 } catch (final IllegalArgumentException illegalArgumentException) {
671 handleIllegalArgumentException(KIND_DAEMON_SET, labelSelector, illegalArgumentException);
672 } catch (final RuntimeException runtimeException) {
673 handleRuntimeException(KIND_DAEMON_SET, labelSelector, runtimeException);
676 logger.error("Unable to find any DaemonSet resources ...");
677 return Collections.emptyList();
681 public List<KubernetesResource> getStatefulSetResources(final ApiClient apiClient, final String labelSelector)
682 throws KubernetesRequestProcessingException {
683 logger.debug("Retrieving StatefulSet using labelSelector: {}", labelSelector);
685 final AppsV1Api appsV1Api = new AppsV1Api(apiClient);
687 final V1StatefulSetList statefulSetList = appsV1Api.listStatefulSetForAllNamespaces(null, null, null,
688 labelSelector, null, null, null, null, timeoutSeconds, DISABLE_WATCH);
690 logger.debug("Received StatefulSets: {}", statefulSetList);
691 return getKubernetesResource(statefulSetList);
693 } catch (final ApiException exception) {
694 handleApiException(KIND_STATEFUL_SET, labelSelector, exception);
695 } catch (final IllegalArgumentException illegalArgumentException) {
696 handleIllegalArgumentException(KIND_STATEFUL_SET, labelSelector, illegalArgumentException);
697 } catch (final RuntimeException runtimeException) {
698 handleRuntimeException(KIND_STATEFUL_SET, labelSelector, runtimeException);
701 logger.error("Unable to find any StatefulSet resources ...");
702 return Collections.emptyList();
706 private List<KubernetesResource> getKubernetesResource(final KubernetesListObject kubernetesListObject) {
707 if (kubernetesListObject != null && kubernetesListObject.getItems() != null) {
708 final List<KubernetesResource> kubernetesResources = new ArrayList<>();
709 final List<? extends KubernetesObject> items = kubernetesListObject.getItems();
710 items.forEach(item -> {
711 final String apiVersion =
712 item.getApiVersion() != null ? item.getApiVersion() : kubernetesListObject.getApiVersion();
713 final String kind = item.getKind() != null ? item.getKind() : kubernetesListObject.getKind();
714 kubernetesResources.add(getKubernetesResource(apiVersion, kind, item.getMetadata()));
716 logger.debug("KubernetesResources found: {}", kubernetesResources);
717 return kubernetesResources;
719 logger.error("kubernetesListObject or items is null {}", kubernetesListObject);
720 return Collections.emptyList();
723 private KubernetesResource getKubernetesResource(final String apiVersion, final String kind,
724 final V1ObjectMeta metadata) {
725 final GroupVersion groupVersion = GroupVersion.parse(apiVersion);
726 final KubernetesResource resource =
727 new KubernetesResource().id(metadata.getUid()).name(metadata.getName()).group(groupVersion.getGroup())
728 .version(groupVersion.getVersion()).kind(kind).resourceVersion(metadata.getResourceVersion())
729 .namespace(metadata.getNamespace() != null ? metadata.getNamespace() : "")
730 .labels(getLabels(metadata.getLabels()));
734 private List<String> getLabels(final Map<String, String> labels) {
735 if (labels != null) {
736 final List<String> result = new ArrayList<>();
737 labels.entrySet().forEach(entry -> {
738 result.add(entry.getKey() + "=" + entry.getValue());
742 return Collections.emptyList();
746 private boolean isJobReady(final V1Job job) {
747 if (job.getStatus() != null && job.getStatus().getConditions() != null) {
748 logger.debug("Received Job with conditions ..");
749 for (final V1JobCondition condition : job.getStatus().getConditions()) {
750 if (JOB_COMPLETE.equalsIgnoreCase(condition.getType())
751 && TRUE_STRING.equalsIgnoreCase(condition.getStatus())) {
752 logger.debug("Job completed successfully ...");
755 if (JOB_FAILED.equalsIgnoreCase(condition.getType())
756 && TRUE_STRING.equalsIgnoreCase(condition.getStatus())) {
757 final String message = "Job failed with reason: " + condition.getReason();
758 logger.error(message);
759 throw new KubernetesRequestProcessingException(message);
765 logger.debug("Job is not ready ...");
769 private boolean isPodReady(final V1Pod pod) {
770 final Optional<V1PodCondition> optional = getPodReadyCondition(pod);
771 if (optional.isPresent()) {
772 final V1PodCondition condition = optional.get();
773 if (TRUE_STRING.equalsIgnoreCase(condition.getStatus())) {
774 logger.debug("Pod is Ready...");
780 logger.debug("Pod is not ready ...");
784 private boolean isServiceReady(final V1Service service) {
785 logger.debug("V1Service is Ready ...");
789 private boolean isDeploymentReady(final V1Deployment deployment) {
790 final V1DeploymentSpec spec = deployment.getSpec();
791 final V1DeploymentStatus status = deployment.getStatus();
793 if (status != null && status.getReplicas() != null && status.getAvailableReplicas() != null && spec != null
794 && spec.getReplicas() != null) {
795 if (spec.getReplicas().intValue() == status.getReplicas().intValue()
796 && status.getAvailableReplicas().intValue() <= spec.getReplicas().intValue()) {
797 logger.debug("Deployment is Ready...");
802 logger.debug("Deployment is not ready ...");
806 private boolean isReplicaSetReady(final V1ReplicaSet replicaSet) {
807 final V1ReplicaSetSpec spec = replicaSet.getSpec();
808 final V1ReplicaSetStatus status = replicaSet.getStatus();
810 if (status != null && status.getReadyReplicas() != null && spec != null && spec.getReplicas() != null) {
811 if (spec.getReplicas().intValue() == status.getReadyReplicas().intValue()) {
812 logger.debug("ReplicaSet is Ready...");
817 logger.debug("ReplicaSet is not ready ...");
821 private boolean isDaemonSetReady(final V1DaemonSet daemonSet) {
823 final V1DaemonSetSpec spec = daemonSet.getSpec();
824 final V1DaemonSetStatus status = daemonSet.getStatus();
826 if (status == null || spec == null) {
827 logger.debug("Found null status/spec \n DaemonSet: {}", daemonSet);
831 if (spec.getUpdateStrategy() != null && spec.getUpdateStrategy().getType() != null) {
832 if (!ROLLING_UPDATE.equalsIgnoreCase(spec.getUpdateStrategy().getType())) {
833 logger.debug("Type is {} returning true", spec.getUpdateStrategy().getType());
838 if (status.getDesiredNumberScheduled() != null && status.getUpdatedNumberScheduled() != null) {
839 if (status.getUpdatedNumberScheduled().intValue() != status.getDesiredNumberScheduled().intValue()) {
840 logger.debug("DaemonSet is not ready {} out of {} expected pods have been scheduled",
841 status.getUpdatedNumberScheduled(), status.getDesiredNumberScheduled());
845 if (spec.getUpdateStrategy() != null && spec.getUpdateStrategy().getRollingUpdate() != null
846 && status.getNumberReady() != null) {
848 final Integer maxUnavailable =
849 getMaxUnavailable(spec.getUpdateStrategy().getRollingUpdate().getMaxUnavailable(),
850 status.getDesiredNumberScheduled());
852 final int expectedReady = status.getDesiredNumberScheduled().intValue() - maxUnavailable.intValue();
853 final int numberReady = status.getNumberReady().intValue();
854 if (!(numberReady >= expectedReady)) {
855 logger.debug("DaemonSet is not ready {} out of {} expected pods are ready", numberReady,
859 logger.debug("DaemonSet is ready {} out of {} expected pods are ready", numberReady, expectedReady);
860 logger.debug("DaemonSet is Ready...");
866 logger.debug("DaemonSet is not ready ...");
870 private boolean isStatefulSetReady(final V1StatefulSet statefulSet) {
871 final V1StatefulSetSpec spec = statefulSet.getSpec();
872 final V1StatefulSetStatus status = statefulSet.getStatus();
874 if (status == null || spec == null) {
875 logger.debug("Found null status/spec \n StatefulSet: {}", statefulSet);
879 final V1StatefulSetUpdateStrategy updateStrategy = spec.getUpdateStrategy();
880 if (updateStrategy != null && updateStrategy.getType() != null) {
881 if (!ROLLING_UPDATE.equalsIgnoreCase(updateStrategy.getType())) {
882 logger.debug("Type is {} returning true", updateStrategy.getType());
886 // Dereference all the pointers because StatefulSets like them
888 // 1 is the default for replicas if not set
890 final V1RollingUpdateStatefulSetStrategy rollingUpdate = updateStrategy.getRollingUpdate();
891 if (rollingUpdate != null && rollingUpdate.getPartition() != null) {
892 partition = updateStrategy.getRollingUpdate().getPartition().intValue();
895 if (spec.getReplicas() != null) {
896 replicas = spec.getReplicas().intValue();
899 final int expectedReplicas = replicas - partition;
901 if (status.getUpdatedReplicas() != null && status.getUpdatedReplicas().intValue() < expectedReplicas) {
902 logger.debug("StatefulSet is not ready. {} out of {} expected pods have been scheduled",
903 status.getUpdatedReplicas(), expectedReplicas);
907 if (status.getReadyReplicas() != null && status.getReadyReplicas().intValue() != replicas) {
908 logger.debug("StatefulSet is not ready. {} out of {} expected pods are ready",
909 status.getReadyReplicas(), replicas);
913 logger.debug("{} out of {} expected pods are ready", status.getReadyReplicas(), replicas);
914 logger.debug("StatefulSet is Ready...");
918 logger.debug("StatefulSet is not ready ...");
922 private Integer getMaxUnavailable(final IntOrString maxUnavailable, final Integer desiredNumberScheduled) {
923 if (maxUnavailable == null) {
924 logger.debug("maxUnavailable value is {}", maxUnavailable);
925 return desiredNumberScheduled;
928 if (maxUnavailable.isInteger()) {
929 logger.debug("maxUnavailable is Integer: {}", maxUnavailable);
930 return maxUnavailable.getIntValue();
933 if (!maxUnavailable.isInteger()) {
934 final Integer maxUnavailableIntValue = getIntegerValue(maxUnavailable);
935 if (maxUnavailableIntValue != null) {
936 return (maxUnavailableIntValue.intValue() * desiredNumberScheduled.intValue()) / 100;
939 logger.debug("maxUnavailableIntValue is null {}", maxUnavailableIntValue);
941 logger.debug("Returning desiredNumberScheduled: {}", desiredNumberScheduled);
942 return desiredNumberScheduled;
945 private Integer getIntegerValue(final IntOrString maxUnavailable) {
947 final String strValue = maxUnavailable.getStrValue();
948 if (strValue != null && strValue.length() > 1) {
949 if (strValue.contains("%")) {
950 final String val = strValue.trim().replace("%", "");
951 return Integer.valueOf(val);
953 logger.debug("maxUnavailable is not a percentage");
955 } catch (final Exception exception) {
956 logger.error("Unable to parse maxUnavailable value: {}", maxUnavailable);
961 private void closeWatchSilently(final Watch<?> watch) {
964 } catch (final IOException exception) {
965 logger.warn("Unexpected IOException while closing watch suppressing exception");
969 private void handleRuntimeException(final String resourceType, final String labelSelector,
970 final RuntimeException runtimeException) {
971 if (runtimeException.getCause() instanceof SocketTimeoutException) {
972 final Throwable cause = runtimeException.getCause();
973 final String message = "Unexpected SocketTimeoutException occurred while getting " + resourceType
974 + " status using labelSelector: " + labelSelector + " message: " + cause.getMessage();
975 logger.error(message, cause);
976 throw new KubernetesRequestTimeOut(message, cause);
978 final String message = "Unexpected RuntimeException occurred while getting " + resourceType
979 + " status using labelSelector: " + labelSelector;
980 logger.error(message, runtimeException);
981 throw new KubernetesRequestProcessingException(message, runtimeException);
984 private void handleApiException(final String resourceType, final String labelSelector,
985 final ApiException exception) {
986 final String message = "Unexpected ApiException occurred while getting " + resourceType
987 + " status using labelSelector: " + labelSelector + " \n response code: " + exception.getCode()
988 + " \n response body: " + exception.getResponseBody();
989 logger.error(message, exception);
990 throw new KubernetesRequestProcessingException(message, exception);
993 private void handleIllegalArgumentException(final String resourceType, final String labelSelector,
994 final IllegalArgumentException illegalArgumentException) {
995 final String message = "Unexpected IllegalArgumentException occurred while getting " + resourceType
996 + " resource using labelSelector: " + labelSelector;
997 logger.error(message, illegalArgumentException);
998 throw new KubernetesRequestProcessingException(message, illegalArgumentException);
1001 private Optional<V1PodCondition> getPodReadyCondition(final V1Pod pod) {
1002 if (pod.getStatus() != null && pod.getStatus().getConditions() != null) {
1003 final List<V1PodCondition> conditions = pod.getStatus().getConditions();
1004 return conditions.stream().filter(condition -> POD_READY.equals(condition.getType()))
1005 .peek(condition -> logger.debug("Found {}", condition)).findFirst();
1008 logger.warn("Unable to find a {} condition {}", POD_READY, pod.getStatus());
1009 return Optional.empty();
1013 * Capturing resource events and objects
1020 * @throws ApiException
1022 private <T> Map<T, String> getReadyResources(final ApiClient apiClient, final Call call, final Type type)
1023 throws ApiException {
1024 final Watch<T> watch = Watch.createWatch(apiClient, call, type);
1025 logger.debug("Listening for {} events ....", type.getTypeName());
1027 final Map<T, String> resources = new HashMap<>();
1029 while (watch.hasNext()) {
1030 final Response<T> next = watch.next();
1031 final T object = next.object;
1032 logger.debug("Received object: {}", object);
1033 final String event = next.type;
1034 logger.debug("Received Event: {}", event);
1035 resources.put(object, event);
1039 closeWatchSilently(watch);
1041 logger.debug("Finished Listening for {} events ....", type.getTypeName());
1046 private static <T> boolean isResourceReady(final T object, final String eventType, final Predicate<T> predicate) {
1048 switch (eventType) {
1049 case EVENT_TYPE_ADDED:
1050 case EVENT_TYPE_MODIFIED:
1051 return predicate.test(object);
1052 case EVENT_TYPE_DELETED:
1053 logger.debug("{} event received marking it as successfully", EVENT_TYPE_DELETED);
1055 case EVENT_TYPE_ERROR:
1056 final String message = "Error event received for " + (object != null ? object.getClass() : "null");
1057 logger.error(message);
1058 logger.debug("{} received: {}", (object != null ? object.getClass() : "null"), object);
1059 throw new KubernetesRequestProcessingException(message);
1062 logger.warn("Unhandled event received ... ");