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 handleApiException(KIND_SERVICE, labelSelector, exception);
399 } catch (final RuntimeException runtimeException) {
400 handleRuntimeException(KIND_SERVICE, labelSelector, runtimeException);
402 logger.debug("Returning false as Service is not Deleted ...");
407 public boolean isPodDeleted(final ApiClient apiClient, final String labelSelector)
408 throws KubernetesRequestProcessingException {
409 logger.debug("Check is Pod deleted by using labelSelector: {}", labelSelector);
411 final CoreV1Api coreV1Api = new CoreV1Api(apiClient);
412 final V1PodList v1PodList = coreV1Api.listPodForAllNamespaces(null, null, null, labelSelector, null, null,
413 null, null, timeoutSeconds, DISABLE_WATCH);
414 logger.debug("Response from list Pod for all Namespaces: {}", v1PodList);
415 return v1PodList.getItems().isEmpty();
416 } catch (final ApiException exception) {
417 handleApiException(KIND_POD, labelSelector, exception);
418 } catch (final RuntimeException runtimeException) {
419 handleRuntimeException(KIND_POD, labelSelector, runtimeException);
421 logger.debug("Returning false as Pod is not Deleted ...");
426 public boolean isJobDeleted(final ApiClient apiClient, final String labelSelector)
427 throws KubernetesRequestProcessingException {
428 logger.debug("Check is Job deleted by using labelSelector: {}", labelSelector);
430 final BatchV1Api batchV1Api = new BatchV1Api(apiClient);
431 final V1JobList v1JobList = batchV1Api.listJobForAllNamespaces(null, null, null, labelSelector, null, null,
432 null, null, timeoutSeconds, DISABLE_WATCH);
433 logger.debug("Response from list Job for all Namespaces: {}", v1JobList);
434 return v1JobList.getItems().isEmpty();
435 } catch (final ApiException exception) {
436 handleApiException(KIND_JOB, labelSelector, exception);
437 } catch (final RuntimeException runtimeException) {
438 handleRuntimeException(KIND_JOB, labelSelector, runtimeException);
440 logger.debug("Returning false as Job is not Deleted ...");
445 public boolean isDeploymentDeleted(final ApiClient apiClient, final String labelSelector)
446 throws KubernetesRequestProcessingException {
447 logger.debug("Check is Deployment deleted by using labelSelector: {}", labelSelector);
449 final AppsV1Api batchV1Api = new AppsV1Api(apiClient);
450 final V1DeploymentList v1DeploymentList = batchV1Api.listDeploymentForAllNamespaces(null, null, null,
451 labelSelector, null, null, null, null, timeoutSeconds, DISABLE_WATCH);
452 logger.debug("Response from list Deployment for all Namespaces: {}", v1DeploymentList);
453 return v1DeploymentList.getItems().isEmpty();
454 } catch (final ApiException exception) {
455 handleApiException(KIND_DEPLOYMENT, labelSelector, exception);
456 } catch (final RuntimeException runtimeException) {
457 handleRuntimeException(KIND_DEPLOYMENT, labelSelector, runtimeException);
459 logger.debug("Returning false as Deployment is not Deleted ...");
464 public boolean isReplicaSetDeleted(final ApiClient apiClient, final String labelSelector)
465 throws KubernetesRequestProcessingException {
466 logger.debug("Check is ReplicaSet deleted by using labelSelector: {}", labelSelector);
468 final AppsV1Api batchV1Api = new AppsV1Api(apiClient);
469 final V1ReplicaSetList v1ReplicaSetList = batchV1Api.listReplicaSetForAllNamespaces(null, null, null,
470 labelSelector, null, null, null, null, timeoutSeconds, DISABLE_WATCH);
471 logger.debug("Response from list ReplicaSet for all Namespaces: {}", v1ReplicaSetList);
472 return v1ReplicaSetList.getItems().isEmpty();
473 } catch (final ApiException exception) {
474 handleApiException(KIND_REPLICA_SET, labelSelector, exception);
475 } catch (final RuntimeException runtimeException) {
476 handleRuntimeException(KIND_REPLICA_SET, labelSelector, runtimeException);
478 logger.debug("Returning false as ReplicaSet is not Deleted ...");
483 public boolean isDaemonSetDeleted(final ApiClient apiClient, final String labelSelector)
484 throws KubernetesRequestProcessingException {
485 logger.debug("Check is DaemonSet deleted by using labelSelector: {}", labelSelector);
487 final AppsV1Api batchV1Api = new AppsV1Api(apiClient);
488 final V1DaemonSetList v1DaemonSetList = batchV1Api.listDaemonSetForAllNamespaces(null, null, null,
489 labelSelector, null, null, null, null, timeoutSeconds, DISABLE_WATCH);
490 logger.debug("Response from list DaemonSet for all Namespaces: {}", v1DaemonSetList);
491 return v1DaemonSetList.getItems().isEmpty();
492 } catch (final ApiException exception) {
493 handleApiException(KIND_DAEMON_SET, labelSelector, exception);
494 } catch (final RuntimeException runtimeException) {
495 handleRuntimeException(KIND_DAEMON_SET, labelSelector, runtimeException);
497 logger.debug("Returning false as DaemonSet is not Deleted ...");
502 public boolean isStatefulSetDeleted(final ApiClient apiClient, final String labelSelector)
503 throws KubernetesRequestProcessingException {
504 logger.debug("Check is StatefulSet deleted by using labelSelector: {}", labelSelector);
506 final AppsV1Api batchV1Api = new AppsV1Api(apiClient);
507 final V1StatefulSetList v1StatefulSetList = batchV1Api.listStatefulSetForAllNamespaces(null, null, null,
508 labelSelector, null, null, null, null, timeoutSeconds, DISABLE_WATCH);
509 logger.debug("Response from list StatefulSet for all Namespaces: {}", v1StatefulSetList);
510 return v1StatefulSetList.getItems().isEmpty();
511 } catch (final ApiException exception) {
512 handleApiException(KIND_STATEFUL_SET, labelSelector, exception);
513 } catch (final RuntimeException runtimeException) {
514 handleRuntimeException(KIND_STATEFUL_SET, labelSelector, runtimeException);
516 logger.debug("Returning false as StatefulSet is not Deleted ...");
522 public List<KubernetesResource> getJobResources(final ApiClient apiClient, final String labelSelector)
523 throws KubernetesRequestProcessingException {
524 logger.debug("Retrieving Jobs using labelSelector: {}", labelSelector);
526 final BatchV1Api batchV1Api = new BatchV1Api(apiClient);
527 final V1JobList jobList = batchV1Api.listJobForAllNamespaces(null, null, null, labelSelector, null, null,
528 null, null, timeoutSeconds, DISABLE_WATCH);
530 logger.debug("Received Jobs: {}", jobList);
531 return getKubernetesResource(jobList);
533 } catch (final ApiException exception) {
534 handleApiException(KIND_JOB, labelSelector, exception);
535 } catch (final IllegalArgumentException illegalArgumentException) {
536 handleIllegalArgumentException(KIND_JOB, labelSelector, illegalArgumentException);
537 } catch (final RuntimeException runtimeException) {
538 handleRuntimeException(KIND_JOB, labelSelector, runtimeException);
541 logger.error("Unable to find any job resources ...");
542 return Collections.emptyList();
546 public List<KubernetesResource> getDeploymentResources(final ApiClient apiClient, final String labelSelector)
547 throws KubernetesRequestProcessingException {
548 logger.debug("Retrieving Deployment using labelSelector: {}", labelSelector);
550 final AppsV1Api appsV1Api = new AppsV1Api(apiClient);
551 final V1DeploymentList deploymentList = appsV1Api.listDeploymentForAllNamespaces(null, null, null,
552 labelSelector, null, null, null, null, timeoutSeconds, DISABLE_WATCH);
554 logger.debug("Received Deployments: {}", deploymentList);
555 return getKubernetesResource(deploymentList);
557 } catch (final ApiException exception) {
558 handleApiException(KIND_DEPLOYMENT, labelSelector, exception);
559 } catch (final IllegalArgumentException illegalArgumentException) {
560 handleIllegalArgumentException(KIND_DEPLOYMENT, labelSelector, illegalArgumentException);
561 } catch (final RuntimeException runtimeException) {
562 handleRuntimeException(KIND_DEPLOYMENT, labelSelector, runtimeException);
565 logger.error("Unable to find any Deployment resources ...");
566 return Collections.emptyList();
570 public List<KubernetesResource> getPodResources(final ApiClient apiClient, final String labelSelector)
571 throws KubernetesRequestProcessingException {
572 logger.debug("Retrieving Pod using labelSelector: {}", labelSelector);
574 final CoreV1Api coreV1Api = new CoreV1Api(apiClient);
575 final V1PodList podList = coreV1Api.listPodForAllNamespaces(null, null, null, labelSelector, null, null,
576 null, null, timeoutSeconds, DISABLE_WATCH);
578 logger.debug("Received Pods: {}", podList);
579 return getKubernetesResource(podList);
581 } catch (final ApiException exception) {
582 handleApiException(KIND_POD, labelSelector, exception);
583 } catch (final IllegalArgumentException illegalArgumentException) {
584 handleIllegalArgumentException(KIND_POD, labelSelector, illegalArgumentException);
585 } catch (final RuntimeException runtimeException) {
586 handleRuntimeException(KIND_POD, labelSelector, runtimeException);
589 logger.error("Unable to find any Pod resources ...");
590 return Collections.emptyList();
594 public List<KubernetesResource> getServiceResources(final ApiClient apiClient, final String labelSelector)
595 throws KubernetesRequestProcessingException {
596 logger.debug("Retrieving Service using labelSelector: {}", labelSelector);
598 final CoreV1Api coreV1Api = new CoreV1Api(apiClient);
599 final V1ServiceList serviceList = coreV1Api.listServiceForAllNamespaces(null, null, null, labelSelector,
600 null, null, null, null, timeoutSeconds, DISABLE_WATCH);
602 logger.debug("Received Services: {}", serviceList);
603 return getKubernetesResource(serviceList);
605 } catch (final ApiException exception) {
606 handleApiException(KIND_SERVICE, labelSelector, exception);
607 } catch (final IllegalArgumentException illegalArgumentException) {
608 handleIllegalArgumentException(KIND_SERVICE, labelSelector, illegalArgumentException);
609 } catch (final RuntimeException runtimeException) {
610 handleRuntimeException(KIND_SERVICE, labelSelector, runtimeException);
613 logger.error("Unable to find any Service resources ...");
614 return Collections.emptyList();
618 public List<KubernetesResource> getReplicaSetResources(final ApiClient apiClient, final String labelSelector)
619 throws KubernetesRequestProcessingException {
620 logger.debug("Retrieving ReplicaSet using labelSelector: {}", labelSelector);
622 final AppsV1Api appsV1Api = new AppsV1Api(apiClient);
623 final V1ReplicaSetList replicaSetList = appsV1Api.listReplicaSetForAllNamespaces(null, null, null,
624 labelSelector, null, null, null, null, timeoutSeconds, DISABLE_WATCH);
626 logger.debug("Received ReplicaSets: {}", replicaSetList);
627 return getKubernetesResource(replicaSetList);
629 } catch (final ApiException exception) {
630 handleApiException(KIND_REPLICA_SET, labelSelector, exception);
631 } catch (final IllegalArgumentException illegalArgumentException) {
632 handleIllegalArgumentException(KIND_REPLICA_SET, labelSelector, illegalArgumentException);
633 } catch (final RuntimeException runtimeException) {
634 handleRuntimeException(KIND_REPLICA_SET, labelSelector, runtimeException);
637 logger.error("Unable to find any ReplicaSet resources ...");
638 return Collections.emptyList();
642 public List<KubernetesResource> getDaemonSetResources(final ApiClient apiClient, final String labelSelector)
643 throws KubernetesRequestProcessingException {
644 logger.debug("Retrieving DaemonSet using labelSelector: {}", labelSelector);
646 final AppsV1Api appsV1Api = new AppsV1Api(apiClient);
648 final V1DaemonSetList daemonSetList = appsV1Api.listDaemonSetForAllNamespaces(null, null, null,
649 labelSelector, null, null, null, null, timeoutSeconds, DISABLE_WATCH);
651 logger.debug("Received DaemonSets: {}", daemonSetList);
652 return getKubernetesResource(daemonSetList);
654 } catch (final ApiException exception) {
655 handleApiException(KIND_DAEMON_SET, labelSelector, exception);
656 } catch (final IllegalArgumentException illegalArgumentException) {
657 handleIllegalArgumentException(KIND_DAEMON_SET, labelSelector, illegalArgumentException);
658 } catch (final RuntimeException runtimeException) {
659 handleRuntimeException(KIND_DAEMON_SET, labelSelector, runtimeException);
662 logger.error("Unable to find any DaemonSet resources ...");
663 return Collections.emptyList();
667 public List<KubernetesResource> getStatefulSetResources(final ApiClient apiClient, final String labelSelector)
668 throws KubernetesRequestProcessingException {
669 logger.debug("Retrieving StatefulSet using labelSelector: {}", labelSelector);
671 final AppsV1Api appsV1Api = new AppsV1Api(apiClient);
673 final V1StatefulSetList statefulSetList = appsV1Api.listStatefulSetForAllNamespaces(null, null, null,
674 labelSelector, null, null, null, null, timeoutSeconds, DISABLE_WATCH);
676 logger.debug("Received StatefulSets: {}", statefulSetList);
677 return getKubernetesResource(statefulSetList);
679 } catch (final ApiException exception) {
680 handleApiException(KIND_STATEFUL_SET, labelSelector, exception);
681 } catch (final IllegalArgumentException illegalArgumentException) {
682 handleIllegalArgumentException(KIND_STATEFUL_SET, labelSelector, illegalArgumentException);
683 } catch (final RuntimeException runtimeException) {
684 handleRuntimeException(KIND_STATEFUL_SET, labelSelector, runtimeException);
687 logger.error("Unable to find any StatefulSet resources ...");
688 return Collections.emptyList();
692 private List<KubernetesResource> getKubernetesResource(final KubernetesListObject kubernetesListObject) {
693 if (kubernetesListObject != null && kubernetesListObject.getItems() != null) {
694 final List<KubernetesResource> kubernetesResources = new ArrayList<>();
695 final List<? extends KubernetesObject> items = kubernetesListObject.getItems();
696 items.forEach(item -> {
697 final String apiVersion =
698 item.getApiVersion() != null ? item.getApiVersion() : kubernetesListObject.getApiVersion();
699 final String kind = item.getKind() != null ? item.getKind() : kubernetesListObject.getKind();
700 kubernetesResources.add(getKubernetesResource(apiVersion, kind, item.getMetadata()));
702 logger.debug("KubernetesResources found: {}", kubernetesResources);
703 return kubernetesResources;
705 logger.error("kubernetesListObject or items is null {}", kubernetesListObject);
706 return Collections.emptyList();
709 private KubernetesResource getKubernetesResource(final String apiVersion, final String kind,
710 final V1ObjectMeta metadata) {
711 final GroupVersion groupVersion = GroupVersion.parse(apiVersion);
712 return new KubernetesResource().id(metadata.getUid()).name(metadata.getName()).group(groupVersion.getGroup())
713 .version(groupVersion.getVersion()).kind(kind).resourceVersion(metadata.getResourceVersion())
714 .namespace(metadata.getNamespace() != null ? metadata.getNamespace() : "")
715 .labels(getLabels(metadata.getLabels()));
718 private List<String> getLabels(final Map<String, String> labels) {
719 if (labels != null) {
720 final List<String> result = new ArrayList<>();
721 labels.entrySet().forEach(entry -> result.add(entry.getKey() + "=" + entry.getValue()));
724 return Collections.emptyList();
728 private boolean isJobReady(final V1Job job) {
729 if (job.getStatus() != null && job.getStatus().getConditions() != null) {
730 logger.debug("Received Job with conditions ..");
731 for (final V1JobCondition condition : job.getStatus().getConditions()) {
732 if (JOB_COMPLETE.equalsIgnoreCase(condition.getType())
733 && TRUE_STRING.equalsIgnoreCase(condition.getStatus())) {
734 logger.debug("Job completed successfully ...");
737 if (JOB_FAILED.equalsIgnoreCase(condition.getType())
738 && TRUE_STRING.equalsIgnoreCase(condition.getStatus())) {
739 final String message = "Job failed with reason: " + condition.getReason();
740 logger.error(message);
741 throw new KubernetesRequestProcessingException(message);
747 logger.debug("Job is not ready ...");
751 private boolean isPodReady(final V1Pod pod) {
752 final Optional<V1PodCondition> optional = getPodReadyCondition(pod);
753 if (optional.isPresent()) {
754 final V1PodCondition condition = optional.get();
755 return TRUE_STRING.equalsIgnoreCase(condition.getStatus());
761 private boolean isServiceReady(final V1Service service) {
765 private boolean isDeploymentReady(final V1Deployment deployment) {
766 final V1DeploymentSpec spec = deployment.getSpec();
767 final V1DeploymentStatus status = deployment.getStatus();
769 if (status == null || status.getReplicas() == null || status.getAvailableReplicas() == null) {
770 logger.debug("AvailableReplicas is null in status");
774 if (spec == null || spec.getReplicas() == null) {
775 logger.debug("Replicas is null in spec");
779 return spec.getReplicas().intValue() == status.getReplicas().intValue()
780 && status.getAvailableReplicas().intValue() <= spec.getReplicas().intValue();
783 private boolean isReplicaSetReady(final V1ReplicaSet replicaSet) {
784 final V1ReplicaSetSpec spec = replicaSet.getSpec();
785 final V1ReplicaSetStatus status = replicaSet.getStatus();
787 if (status == null || status.getReadyReplicas() == null) {
788 logger.debug("ReadyReplicas is null in status");
792 if (spec == null || spec.getReplicas() == null) {
793 logger.debug("Replicas is null in spec");
797 return spec.getReplicas().intValue() == status.getReadyReplicas().intValue();
800 private boolean isDaemonSetReady(final V1DaemonSet daemonSet) {
802 final V1DaemonSetSpec spec = daemonSet.getSpec();
803 final V1DaemonSetStatus status = daemonSet.getStatus();
805 if (status == null || spec == null) {
806 logger.debug("Found null status/spec \n DaemonSet: {}", daemonSet);
810 if (spec.getUpdateStrategy() != null && spec.getUpdateStrategy().getType() != null) {
811 if (!ROLLING_UPDATE.equalsIgnoreCase(spec.getUpdateStrategy().getType())) {
812 logger.debug("Type is {} returning true", spec.getUpdateStrategy().getType());
817 if (status.getDesiredNumberScheduled() != null && status.getUpdatedNumberScheduled() != null) {
818 if (status.getUpdatedNumberScheduled().intValue() != status.getDesiredNumberScheduled().intValue()) {
819 logger.debug("DaemonSet is not ready {} out of {} expected pods have been scheduled",
820 status.getUpdatedNumberScheduled(), status.getDesiredNumberScheduled());
824 if (spec.getUpdateStrategy() != null && spec.getUpdateStrategy().getRollingUpdate() != null
825 && status.getNumberReady() != null) {
827 final Integer maxUnavailable =
828 getMaxUnavailable(spec.getUpdateStrategy().getRollingUpdate().getMaxUnavailable(),
829 status.getDesiredNumberScheduled());
831 final int expectedReady = status.getDesiredNumberScheduled().intValue() - maxUnavailable.intValue();
832 final int numberReady = status.getNumberReady().intValue();
833 if (!(numberReady >= expectedReady)) {
834 logger.debug("DaemonSet is not ready {} out of {} expected pods are ready", numberReady,
838 logger.debug("DaemonSet is ready {} out of {} expected pods are ready", numberReady, expectedReady);
847 private boolean isStatefulSetReady(final V1StatefulSet statefulSet) {
848 final V1StatefulSetSpec spec = statefulSet.getSpec();
849 final V1StatefulSetStatus status = statefulSet.getStatus();
851 if (status == null || spec == null) {
852 logger.debug("Found null status/spec \n StatefulSet: {}", statefulSet);
856 final V1StatefulSetUpdateStrategy updateStrategy = spec.getUpdateStrategy();
857 if (updateStrategy != null && updateStrategy.getType() != null) {
858 if (!ROLLING_UPDATE.equalsIgnoreCase(updateStrategy.getType())) {
859 logger.debug("Type is {} returning true", updateStrategy.getType());
863 // Dereference all the pointers because StatefulSets like them
865 // 1 is the default for replicas if not set
867 final V1RollingUpdateStatefulSetStrategy rollingUpdate = updateStrategy.getRollingUpdate();
868 if (rollingUpdate != null && rollingUpdate.getPartition() != null) {
869 partition = updateStrategy.getRollingUpdate().getPartition().intValue();
872 if (spec.getReplicas() != null) {
873 replicas = spec.getReplicas().intValue();
876 final int expectedReplicas = replicas - partition;
878 if (status.getUpdatedReplicas() != null && status.getUpdatedReplicas().intValue() < expectedReplicas) {
879 logger.debug("StatefulSet is not ready. {} out of {} expected pods have been scheduled",
880 status.getUpdatedReplicas(), expectedReplicas);
884 if (status.getReadyReplicas() != null && status.getReadyReplicas().intValue() != replicas) {
885 logger.debug("StatefulSet is not ready. {} out of {} expected pods are ready",
886 status.getReadyReplicas(), replicas);
890 logger.debug("{} out of {} expected pods are ready", status.getReadyReplicas(), replicas);
891 logger.debug("StatefulSet is Ready...");
895 logger.debug("StatefulSet is not ready ...");
899 private Integer getMaxUnavailable(final IntOrString maxUnavailable, final Integer desiredNumberScheduled) {
900 if (maxUnavailable == null) {
901 logger.debug("maxUnavailable value is {}", maxUnavailable);
902 return desiredNumberScheduled;
905 if (maxUnavailable.isInteger()) {
906 logger.debug("maxUnavailable is Integer: {}", maxUnavailable);
907 return maxUnavailable.getIntValue();
910 if (!maxUnavailable.isInteger()) {
911 final Integer maxUnavailableIntValue = getIntegerValue(maxUnavailable);
912 if (maxUnavailableIntValue != null) {
913 return (maxUnavailableIntValue.intValue() * desiredNumberScheduled.intValue()) / 100;
916 logger.debug("maxUnavailableIntValue is null {}", maxUnavailableIntValue);
918 logger.debug("Returning desiredNumberScheduled: {}", desiredNumberScheduled);
919 return desiredNumberScheduled;
922 private Integer getIntegerValue(final IntOrString maxUnavailable) {
924 final String strValue = maxUnavailable.getStrValue();
925 if (strValue != null && strValue.length() > 1) {
926 if (strValue.contains("%")) {
927 final String val = strValue.trim().replace("%", "");
928 return Integer.valueOf(val);
930 logger.debug("maxUnavailable is not a percentage");
932 } catch (final Exception exception) {
933 logger.error("Unable to parse maxUnavailable value: {}", maxUnavailable);
938 private void closeWatchSilently(final Watch<?> watch) {
941 } catch (final IOException exception) {
942 logger.warn("Unexpected IOException while closing watch suppressing exception");
946 private void handleRuntimeException(final String resourceType, final String labelSelector,
947 final RuntimeException runtimeException) {
948 if (runtimeException.getCause() instanceof SocketTimeoutException) {
949 final Throwable cause = runtimeException.getCause();
950 final String message = "Unexpected SocketTimeoutException occurred while getting " + resourceType
951 + " status using labelSelector: " + labelSelector + " message: " + cause.getMessage();
952 logger.error(message, cause);
953 throw new KubernetesRequestTimeOut(message, cause);
955 final String message = "Unexpected RuntimeException occurred while getting " + resourceType
956 + " status using labelSelector: " + labelSelector;
957 logger.error(message, runtimeException);
958 throw new KubernetesRequestProcessingException(message, runtimeException);
961 private void handleApiException(final String resourceType, final String labelSelector,
962 final ApiException exception) {
963 final String message = "Unexpected ApiException occurred while getting " + resourceType
964 + " status using labelSelector: " + labelSelector + " \n response code: " + exception.getCode()
965 + " \n response body: " + exception.getResponseBody();
966 logger.error(message, exception);
967 throw new KubernetesRequestProcessingException(message, exception);
970 private void handleIllegalArgumentException(final String resourceType, final String labelSelector,
971 final IllegalArgumentException illegalArgumentException) {
972 final String message = "Unexpected IllegalArgumentException occurred while getting " + resourceType
973 + " resource using labelSelector: " + labelSelector;
974 logger.error(message, illegalArgumentException);
975 throw new KubernetesRequestProcessingException(message, illegalArgumentException);
978 private Optional<V1PodCondition> getPodReadyCondition(final V1Pod pod) {
979 if (pod.getStatus() != null && pod.getStatus().getConditions() != null) {
980 final List<V1PodCondition> conditions = pod.getStatus().getConditions();
981 return conditions.stream().filter(condition -> POD_READY.equals(condition.getType()))
982 .peek(condition -> logger.debug("Found {}", condition)).findFirst();
985 logger.warn("Unable to find a {} condition {}", POD_READY, pod.getStatus());
986 return Optional.empty();
990 * Capturing resource events and objects
997 * @throws ApiException
999 private <T> Map<T, String> getReadyResources(final ApiClient apiClient, final Call call, final Type type)
1000 throws ApiException {
1001 final Watch<T> watch = Watch.createWatch(apiClient, call, type);
1002 logger.debug("Listening for {} events ....", type.getTypeName());
1004 final Map<T, String> resources = new HashMap<>();
1006 while (watch.hasNext()) {
1007 final Response<T> next = watch.next();
1008 final T object = next.object;
1009 logger.debug("Received object: {}", object);
1010 final String event = next.type;
1011 logger.debug("Received Event: {}", event);
1012 resources.put(object, event);
1016 closeWatchSilently(watch);
1018 logger.debug("Finished Listening for {} events ....", type.getTypeName());
1023 private static <T> boolean isResourceReady(final T object, final String eventType, final Predicate<T> predicate) {
1025 switch (eventType) {
1026 case EVENT_TYPE_ADDED:
1027 case EVENT_TYPE_MODIFIED:
1028 final boolean isReady = predicate.test(object);
1029 logger.debug("{} is {} ...", object != null ? object.getClass().getSimpleName() : object,
1030 isReady ? "ready" : " not ready");
1032 case EVENT_TYPE_DELETED:
1033 logger.debug("{} event received marking it as successfully", EVENT_TYPE_DELETED);
1035 case EVENT_TYPE_ERROR:
1036 final String message = "Error event received for " + (object != null ? object.getClass() : "null");
1037 logger.error(message);
1038 logger.debug("{} received: {}", (object != null ? object.getClass() : "null"), object);
1039 throw new KubernetesRequestProcessingException(message);
1042 logger.warn("Unhandled event received ... ");