All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.fabric8.kubernetes.client.dsl.base.OperationSupport Maven / Gradle / Ivy

/**
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.fabric8.kubernetes.client.dsl.base;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.fabric8.kubernetes.api.model.DeleteOptions;
import io.fabric8.kubernetes.api.model.DeletionPropagation;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.Preconditions;
import io.fabric8.kubernetes.api.model.Status;
import io.fabric8.kubernetes.api.model.StatusBuilder;
import io.fabric8.kubernetes.api.model.autoscaling.v1.Scale;
import io.fabric8.kubernetes.api.model.extensions.DeploymentRollback;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.http.HttpClient;
import io.fabric8.kubernetes.client.http.HttpRequest;
import io.fabric8.kubernetes.client.http.HttpResponse;
import io.fabric8.kubernetes.client.internal.VersionUsageUtils;
import io.fabric8.kubernetes.client.utils.ExponentialBackoffIntervalCalculator;
import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil;
import io.fabric8.kubernetes.client.utils.Serialization;
import io.fabric8.kubernetes.client.utils.URLUtils;
import io.fabric8.kubernetes.client.utils.Utils;
import io.fabric8.zjsonpatch.JsonDiff;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static io.fabric8.kubernetes.client.internal.PatchUtils.patchMapper;

public class OperationSupport {

  public static final String JSON = "application/json";
  public static final String JSON_PATCH = "application/json-patch+json";
  public static final String STRATEGIC_MERGE_JSON_PATCH = "application/strategic-merge-patch+json";
  public static final String JSON_MERGE_PATCH = "application/merge-patch+json";
  
  protected static final ObjectMapper JSON_MAPPER = Serialization.jsonMapper();
  private static final Logger LOG = LoggerFactory.getLogger(OperationSupport.class);
  private static final String CLIENT_STATUS_FLAG = "CLIENT_STATUS_FLAG";
  private static final int maxRetryIntervalExponent = 5;

  protected OperationContext context;
  protected final HttpClient httpClient;
  protected final Config config;
  protected final String resourceT;
  protected String namespace;
  protected String name;
  protected String apiGroupName;
  protected String apiGroupVersion;
  protected boolean dryRun;
  private final ExponentialBackoffIntervalCalculator retryIntervalCalculator;
  private final int requestRetryBackoffLimit;

  public OperationSupport() {
    this (new OperationContext());
  }

  public OperationSupport(HttpClient client, Config config) {
    this(new OperationContext().withHttpClient(client).withConfig(config));
  }

  public OperationSupport(OperationContext ctx) {
    this.context = ctx;
    this.httpClient = ctx.getClient();
    this.config = ctx.getConfig();
    this.resourceT = ctx.getPlural();
    this.namespace = ctx.getNamespace();
    this.name = ctx.getName() ;
    this.apiGroupName = ctx.getApiGroupName();
    this.dryRun = ctx.getDryRun();
    if (ctx.getApiGroupVersion() != null) {
      this.apiGroupVersion = ctx.getApiGroupVersion();
    } else if (ctx.getConfig() != null) {
      this.apiGroupVersion = ctx.getConfig().getApiVersion();
    } else {
      this.apiGroupVersion = "v1";
    }

    final int requestRetryBackoffInterval;
    if (ctx.getConfig() != null) {
      requestRetryBackoffInterval = ctx.getConfig().getRequestRetryBackoffInterval();
      this.requestRetryBackoffLimit = ctx.getConfig().getRequestRetryBackoffLimit();
    } else {
      requestRetryBackoffInterval = Config.DEFAULT_REQUEST_RETRY_BACKOFFINTERVAL;
      this.requestRetryBackoffLimit = Config.DEFAULT_REQUEST_RETRY_BACKOFFLIMIT;
    }
    this.retryIntervalCalculator = new ExponentialBackoffIntervalCalculator(requestRetryBackoffInterval, maxRetryIntervalExponent);
  }

  public String getAPIGroupName() {
    return apiGroupName;
  }

  public String getAPIGroupVersion() {
    return apiGroupVersion;
  }

  public String getResourceT() {
    return resourceT;
  }

  public String getNamespace() {
    return namespace;
  }

  public String getName() {
    return name;
  }

  public boolean isResourceNamespaced() {
    return true;
  }

  public URL getRootUrl() {
    try {
      if (!Utils.isNullOrEmpty(apiGroupName)) {
        return new URL(URLUtils.join(config.getMasterUrl().toString(), "apis", apiGroupName, apiGroupVersion));
      }
      return new URL(URLUtils.join(config.getMasterUrl().toString(), "api", apiGroupVersion));
    } catch (MalformedURLException e) {
      throw KubernetesClientException.launderThrowable(e);
    }
  }

  public URL getNamespacedUrl(String namespace) throws MalformedURLException {
    URL requestUrl = getRootUrl();
    if (!isResourceNamespaced()) {
      //if resource is not namespaced don't even bother to check the namespace.
    } else if (Utils.isNotNullOrEmpty(namespace)) {
      requestUrl = new URL(URLUtils.join(requestUrl.toString(), "namespaces", namespace));
    }
    requestUrl = new URL(URLUtils.join(requestUrl.toString(), resourceT));
    return requestUrl;
  }

  public URL getNamespacedUrl() throws MalformedURLException {
    return getNamespacedUrl(getNamespace());
  }

  public  URL getNamespacedUrl(T item) throws MalformedURLException {
    return getNamespacedUrl(checkNamespace(item));
  }

  public URL getResourceUrl(String namespace, String name) throws MalformedURLException {
    return getResourceUrl(namespace, name, false);
  }
  
  public URL getResourceUrl(String namespace, String name, boolean status) throws MalformedURLException {
    if (name == null) {
      if (status) {
        throw new KubernetesClientException("name not specified for an operation requiring one.");
      }
      return getNamespacedUrl(namespace);
    }
    if (status) {
      return new URL(URLUtils.join(getNamespacedUrl(namespace).toString(), name, "status"));
    }
    return new URL(URLUtils.join(getNamespacedUrl(namespace).toString(), name));
  }

  public URL getResourceUrl() throws MalformedURLException {
    if (name == null) {
      return getNamespacedUrl();
    }
    return new URL(URLUtils.join(getNamespacedUrl().toString(), name));
  }

  public URL getResourceURLForWriteOperation(URL resourceURL) throws MalformedURLException {
    if (dryRun) {
      return new URL(URLUtils.join(resourceURL.toString(), "?dryRun=All"));
    }
    return resourceURL;
  }

  public URL getResourceURLForPatchOperation(URL resourceUrl, PatchContext patchContext) throws MalformedURLException {
    if (patchContext != null) {
      String url = resourceUrl.toString();
      if (patchContext.getForce() != null) {
        url = URLUtils.join(url, "?force=" + patchContext.getForce());
      }
      if ((patchContext.getDryRun() != null && !patchContext.getDryRun().isEmpty()) || dryRun) {
        url = URLUtils.join(url, "?dryRun=All");
      }
      if (patchContext.getFieldManager() != null) {
        url = URLUtils.join(url, "?fieldManager=" + patchContext.getFieldManager());
      }
      return new URL(url);
    }
    return resourceUrl;
  }

  protected  String checkNamespace(T item) {
    String operationNs = getNamespace();
    String itemNs = (item instanceof HasMetadata) ? KubernetesResourceUtil.getNamespace((HasMetadata)item) : null;
    if (Utils.isNullOrEmpty(operationNs) && Utils.isNullOrEmpty(itemNs)) {
      if (!isResourceNamespaced()) {
        return null;
      } else {
        throw new KubernetesClientException("namespace not specified for an operation requiring one.");
      }
    } else if (Utils.isNullOrEmpty(itemNs)) {
      return operationNs;
    } else if (Utils.isNullOrEmpty(operationNs)) {
      return itemNs;
    } else if (itemNs.equals(operationNs)) {
      return itemNs;
    }
    throw new KubernetesClientException("Namespace mismatch. Item namespace:" + itemNs + ". Operation namespace:" + operationNs + ".");
  }

  protected  String checkName(T item) {
    String operationName = getName();
    ObjectMeta metadata = item instanceof HasMetadata ? ((HasMetadata) item).getMetadata() : null;
    String itemName = metadata != null ? metadata.getName() : null;
    if (Utils.isNullOrEmpty(operationName) && Utils.isNullOrEmpty(itemName)) {
      return null;
    } else if (Utils.isNullOrEmpty(itemName)) {
      return operationName;
    } else if (Utils.isNullOrEmpty(operationName)) {
      return itemName;
    } else if (itemName.equals(operationName)) {
      return itemName;
    }
    throw new KubernetesClientException("Name mismatch. Item name:" + itemName + ". Operation name:" + operationName + ".");
  }

  protected  T handleMetric(String resourceUrl, Class type) throws InterruptedException, IOException {
      HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder()
        .uri(resourceUrl);
      return handleResponse(requestBuilder, type);
  }

  protected  void handleDelete(T resource, long gracePeriodSeconds, DeletionPropagation propagationPolicy, String resourceVersion, boolean cascading) throws InterruptedException, IOException {
    handleDelete(getResourceURLForWriteOperation(getResourceUrl(checkNamespace(resource), checkName(resource))), gracePeriodSeconds, propagationPolicy, resourceVersion, cascading);
  }

  protected void handleDelete(URL requestUrl, long gracePeriodSeconds, DeletionPropagation propagationPolicy, String resourceVersion, boolean cascading) throws InterruptedException, IOException {
    DeleteOptions deleteOptions = new DeleteOptions();
    if (gracePeriodSeconds >= 0) {
      deleteOptions.setGracePeriodSeconds(gracePeriodSeconds);
    }
    if (resourceVersion != null) {
      deleteOptions.setPreconditions(new Preconditions(resourceVersion, null));
    }
    /*
     * Either the propagation policy or the orphan dependent (deprecated) property must be set, but not both.
     */
    if (propagationPolicy != null) {
      deleteOptions.setPropagationPolicy(propagationPolicy.toString());
    } else {
      deleteOptions.setOrphanDependents(!cascading);
    }

    if (dryRun) {
      deleteOptions.setDryRun(Collections.singletonList("All"));
    }

    HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder().delete(JSON, JSON_MAPPER.writeValueAsString(deleteOptions)).url(requestUrl);
    handleResponse(requestBuilder, null, Collections.emptyMap());
  }


  /**
   * Create a resource.
   *
   * @param resource resource provided
   * @param outputType resource type you want as output
   * @param  template argument for output type
   * @param  template argument for resource
   *
   * @return returns de-serialized version of apiserver response in form of type provided
   * @throws InterruptedException Interrupted Exception
   * @throws IOException IOException
   */
  protected  T handleCreate(I resource, Class outputType) throws InterruptedException, IOException {
    HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder()
        .post(JSON, JSON_MAPPER.writeValueAsString(resource))
        .url(getResourceURLForWriteOperation(getResourceUrl(checkNamespace(resource), null)));
    return handleResponse(requestBuilder, outputType, Collections.emptyMap());
  }


  /**
   * Replace a resource.
   *
   * @param updated updated object
   * @param type type of the object provided
   * @param status if this is only the status subresource
   * @param  template argument provided
   *
   * @return returns de-serialized version of api server response
   * @throws InterruptedException Interrupted Exception
   * @throws IOException IOException
   */
  protected  T handleUpdate(T updated, Class type, boolean status) throws InterruptedException, IOException {
    return handleUpdate(updated, type, Collections.emptyMap(), status);
  }

  /**
   * Update a resource, optionally performing placeholder substitution to the response.
   *
   * @param updated updated object
   * @param type type of object provided
   * @param parameters a HashMap containing parameters for processing object
   * @param status if this is only the status subresource
   * @param  template argument provided
   *
   * @return returns de-serialized version of api server response.
   * @throws InterruptedException Interrupted Exception
   * @throws IOException IOException
   */
  protected  T handleUpdate(T updated, Class type, Map parameters, boolean status) throws InterruptedException, IOException {
    HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder()
        .put(JSON, JSON_MAPPER.writeValueAsString(updated))
        .url(getResourceURLForWriteOperation(getResourceUrl(checkNamespace(updated), checkName(updated), status)));
    return handleResponse(requestBuilder, type, parameters);
  }

  /**
   * Send an http patch and handle the response.
   * 
   * If current is not null and patchContext does not specify a patch type, then a JSON patch is assumed.  Otherwise a STRATEGIC MERGE is assumed.
   *
   * @param patchContext patch options for patch request
   * @param current current object
   * @param updated updated object
   * @param type type of object
   * @param status if this is only the status subresource
   * @param  template argument provided
   *
   * @return returns de-serialized version of api server response
   * @throws InterruptedException Interrupted Exception
   * @throws IOException IOException
   */
  protected  T handlePatch(PatchContext patchContext, T current, T updated, Class type, boolean status) throws InterruptedException, IOException {
    String patchForUpdate = null;
    if (current != null && (patchContext == null || patchContext.getPatchType() == PatchType.JSON)) {
      patchForUpdate = JSON_MAPPER.writeValueAsString(JsonDiff.asJson(patchMapper().valueToTree(current), patchMapper().valueToTree(updated)));
      if (patchContext == null) {
        patchContext = new PatchContext.Builder().withPatchType(PatchType.JSON).build();
      }
    } else {
      patchForUpdate = Serialization.asJson(updated);
      current = updated; // use the updated to determine the path
    }
    return handlePatch(patchContext, current, patchForUpdate, type, status);
  }

  /**
   * Send an http patch and handle the response.
   *
   * @param current current object
   * @param patchForUpdate updated object spec as json string
   * @param type type of object
   * @param  template argument provided
   *
   * @return returns de-serialized version of api server response
   * @throws InterruptedException Interrupted Exception
   * @throws IOException IOException
   */
  protected  T handlePatch(T current, Map patchForUpdate, Class type) throws InterruptedException, IOException {
    return handlePatch(new PatchContext.Builder().withPatchType(PatchType.STRATEGIC_MERGE).build(), current,
        JSON_MAPPER.writeValueAsString(patchForUpdate), type, false);
  }

  /**
   * Send an http patch and handle the response.
   *
   * @param patchContext patch options for patch request
   * @param current current object
   * @param patchForUpdate Patch string
   * @param type type of object
   * @param status if this is only the status subresource
   * @param  template argument provided
   * @return returns de-serialized version of api server response
   * @throws InterruptedException Interrupted Exception
   * @throws IOException IOException in case of network errors
   */
  protected  T handlePatch(PatchContext patchContext, T current, String patchForUpdate, Class type, boolean status) throws InterruptedException, IOException {
    String bodyContentType = getContentTypeFromPatchContextOrDefault(patchContext);
    HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder()
        .patch(bodyContentType, patchForUpdate)
        .url(getResourceURLForPatchOperation(getResourceUrl(checkNamespace(current), checkName(current), status),
            patchContext));
    return handleResponse(requestBuilder, type, Collections.emptyMap());
  }

  /**
   * Replace Scale of specified Kubernetes Resource
   *
   * @param resourceUrl Kubernetes resource URL
   * @param scale Scale object which we want to inject
   * @return updated Scale object
   * @throws InterruptedException in case thread is interrupted
   * @throws IOException in some other I/O problem
   */
  protected Scale handleScale(String resourceUrl, Scale scale) throws InterruptedException, IOException {
    HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder().uri(resourceUrl + "/scale");
    if (scale != null) {
      requestBuilder.put(JSON, JSON_MAPPER.writeValueAsString(scale));
    }
    return handleResponse(requestBuilder, Scale.class);
  }

  /**
   * Create rollback of a Deployment
   *
   * @param resourceUrl resource url
   * @param deploymentRollback DeploymentRollback resource
   * @return Status
   * @throws InterruptedException in case thread is interrupted
   * @throws IOException in some other I/O problem
   */
  protected Status handleDeploymentRollback(String resourceUrl, DeploymentRollback deploymentRollback) throws InterruptedException, IOException {
    HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder().uri(resourceUrl + "/rollback").post(JSON, JSON_MAPPER.writeValueAsString(deploymentRollback));
    return handleResponse(requestBuilder, Status.class);
  }

  /**
   * Send an http get.
   *
   * @param resourceUrl resource URL to be processed
   * @param type type of resource
   * @param  template argument provided
   *
   * @return returns a deserialized object as api server response of provided type.
   * @throws InterruptedException Interrupted Exception
   * @throws IOException IOException
   */
  protected  T handleGet(URL resourceUrl, Class type) throws InterruptedException, IOException {
    return handleGet(resourceUrl, type, Collections.emptyMap());
  }
  
  /**
   * Send a raw get - where the type should be one of String, Reader, InputStream
   * 
* NOTE: Currently does not utilize the retry logic */ protected T handleRawGet(URL resourceUrl, Class type) throws IOException{ HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder().url(resourceUrl); HttpRequest request = requestBuilder.build(); HttpResponse response = httpClient.send(request, type); assertResponseCode(request, response); return response.body(); } /** * Send an http, optionally performing placeholder substitution to the response. * * @param resourceUrl resource URL to be processed * @param type type of resource * @param parameters A HashMap of strings containing parameters to be passed in request * @param template argument provided * * @return Returns a deserialized object as api server response of provided type. * @throws InterruptedException Interrupted Exception * @throws IOException IOException */ protected T handleGet(URL resourceUrl, Class type, Map parameters) throws InterruptedException, IOException { HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder().url(resourceUrl); return handleResponse(requestBuilder, type, parameters); } /** * Send an http request and handle the response. * * @param requestBuilder Request Builder object * @param type type of resource * @param template argument provided * * @return Returns a de-serialized object as api server response of provided type. * @throws InterruptedException Interrupted Exception * @throws IOException IOException */ protected T handleResponse(HttpRequest.Builder requestBuilder, Class type) throws InterruptedException, IOException { return handleResponse(requestBuilder, type, Collections.emptyMap()); } /** * Send an http request and handle the response, optionally performing placeholder substitution to the response. * * @param requestBuilder request builder * @param type type of object * @param parameters a hashmap containing parameters * @param template argument provided * * @return Returns a de-serialized object as api server response of provided type. * @throws InterruptedException Interrupted Exception * @throws IOException IOException */ protected T handleResponse(HttpRequest.Builder requestBuilder, Class type, Map parameters) throws InterruptedException, IOException { return handleResponse(httpClient, requestBuilder, type, parameters); } /** * Send an http request and handle the response. * * @param client the client * @param requestBuilder request builder * @param type type of object * @param template argument provided * * @return Returns a de-serialized object as api server response of provided type. * @throws InterruptedException Interrupted Exception * @throws IOException IOException */ protected T handleResponse(HttpClient client, HttpRequest.Builder requestBuilder, Class type) throws InterruptedException, IOException { return handleResponse(client, requestBuilder, type, Collections.emptyMap()); } /** * Send an http request and handle the response, optionally performing placeholder substitution to the response. * * @param client the client * @param requestBuilder Request builder * @param type Type of object provided * @param parameters A hashmap containing parameters * @param Template argument provided * * @return Returns a de-serialized object as api server response of provided type. * @throws InterruptedException Interrupted Exception * @throws IOException IOException */ protected T handleResponse(HttpClient client, HttpRequest.Builder requestBuilder, Class type, Map parameters) throws InterruptedException, IOException { VersionUsageUtils.log(this.resourceT, this.apiGroupVersion); HttpRequest request = requestBuilder.build(); HttpResponse response = retryWithExponentialBackoff(client, request); try (InputStream bodyInputStream = response.body()) { assertResponseCode(request, response); if (type != null) { return Serialization.unmarshal(bodyInputStream, type, parameters); } else { return null; } } catch (Exception e) { if (e instanceof KubernetesClientException) { throw e; } throw requestException(request, e); } } protected HttpResponse retryWithExponentialBackoff(HttpClient client, HttpRequest request) throws InterruptedException, IOException { int numRetries = 0; long retryInterval; while (true) { try { HttpResponse response = client.send(request, InputStream.class); if (numRetries < requestRetryBackoffLimit && response.code() >= 500) { retryInterval = retryIntervalCalculator.getInterval(numRetries); LOG.debug("HTTP operation on url: {} should be retried as the response code was {}, retrying after {} millis", request.uri(), response.code(), retryInterval); if (response.body() != null) { response.body().close(); } } else { return response; } } catch (IOException ie) { if (numRetries < requestRetryBackoffLimit) { retryInterval = retryIntervalCalculator.getInterval(numRetries); LOG.debug(String.format("HTTP operation on url: %s should be retried after %d millis because of IOException", request.uri(), retryInterval), ie); } else { throw ie; } } Thread.sleep(retryInterval); numRetries++; } } /** * Checks if the response status code is the expected and throws the appropriate KubernetesClientException if not. * * @param request The {#link HttpRequest} object. * @param response The {@link HttpResponse} object. */ protected void assertResponseCode(HttpRequest request, HttpResponse response) { int statusCode = response.code(); String customMessage = config.getErrorMessages().get(statusCode); if (response.isSuccessful()) { return; } else if (customMessage != null) { throw requestFailure(request, createStatus(statusCode, combineMessages(customMessage, createStatus(response)))); } else { throw requestFailure(request, createStatus(response)); } } private String combineMessages(String customMessage, Status defaultStatus) { if (defaultStatus != null) { String message = defaultStatus.getMessage(); if (message != null && message.length() > 0) { return customMessage + " " + message; } } return customMessage; } public static Status createStatus(HttpResponse response) { String statusMessage = ""; int statusCode = response != null ? response.code() : 0; if (response == null) { statusMessage = "No response"; } else { try { String bodyString = response.bodyString(); if (Utils.isNotNullOrEmpty(bodyString)) { Status status = JSON_MAPPER.readValue(bodyString, Status.class); if (status.getCode() == null) { status = new StatusBuilder(status).withCode(statusCode).build(); } return status; } } catch (IOException e) { // ignored } if (response.message() != null) { statusMessage = response.message(); } } return createStatus(statusCode, statusMessage); } public static Status createStatus(int statusCode, String message) { Status status = new StatusBuilder() .withCode(statusCode) .withMessage(message) .build(); status.getAdditionalProperties().put(CLIENT_STATUS_FLAG, "true"); return status; } public static KubernetesClientException requestFailure(HttpRequest request, Status status) { return requestFailure(request, status, null); } public static KubernetesClientException requestFailure(HttpRequest request, Status status, String message) { StringBuilder sb = new StringBuilder(); if(message != null && !message.isEmpty()) { sb.append(message).append(". "); } sb.append("Failure executing: ").append(request.method()) .append(" at: ").append(request.uri()).append("."); if (status.getMessage() != null && !status.getMessage().isEmpty()) { sb.append(" Message: ").append(status.getMessage()).append("."); } if (!status.getAdditionalProperties().containsKey(CLIENT_STATUS_FLAG)) { sb.append(" Received status: ").append(status).append("."); } final RequestMetadata metadata = RequestMetadata.from(request); return new KubernetesClientException(sb.toString(), status.getCode(), status, metadata.group, metadata.version, metadata.plural, metadata.namespace); } public static KubernetesClientException requestException(HttpRequest request, Throwable e, String message) { StringBuilder sb = new StringBuilder(); if (message != null && !message.isEmpty()) { sb.append(message).append(". "); } sb.append("Error executing: ").append(request.method()) .append(" at: ").append(request.uri()) .append(". Cause: ").append(e.getMessage()); final RequestMetadata metadata = RequestMetadata.from(request); return new KubernetesClientException(sb.toString(), e, metadata.group, metadata.version, metadata.plural, metadata.namespace); } public static KubernetesClientException requestException(HttpRequest request, Exception e) { return requestException(request, e, null); } private static class RequestMetadata { private final String group; private final String version; private final String plural; private final String namespace; private final static RequestMetadata EMPTY = new RequestMetadata(null, null, null, null); private RequestMetadata(String group, String version, String plural, String namespace) { this.group = group; this.version = version; this.plural = plural; this.namespace = namespace; } static RequestMetadata from(HttpRequest request) { final List segments = Arrays.asList(request.uri().getRawPath().split("\\/")); switch (segments.size()) { case 4: // cluster URL return new RequestMetadata(segments.get(1), segments.get(2), segments.get(3), null); case 6: // namespaced URL return new RequestMetadata(segments.get(1), segments.get(2), segments.get(5), segments.get(4)); default: return EMPTY; } } } protected static T unmarshal(InputStream is) { return Serialization.unmarshal(is); } protected static T unmarshal(InputStream is, final Class type) { return Serialization.unmarshal(is, type); } protected static T unmarshal(InputStream is, TypeReference type) { return Serialization.unmarshal(is, type); } protected static Map getObjectValueAsMap(T object) { return JSON_MAPPER.convertValue(object, Map.class); } public Config getConfig() { return config; } private String getContentTypeFromPatchContextOrDefault(PatchContext patchContext) { if (patchContext != null && patchContext.getPatchType() != null) { return patchContext.getPatchType().getContentType(); } return STRATEGIC_MERGE_JSON_PATCH; } public R1 restCall(Class result, String... path) { try { URL requestUrl = new URL(config.getMasterUrl()); String url = requestUrl.toString(); if (path != null && path.length > 0) { url = URLUtils.join(url, URLUtils.pathJoin(path)); } HttpRequest.Builder req = httpClient.newHttpRequestBuilder().uri(url); return handleResponse(req, result); } catch (KubernetesClientException e) { if (e.getCode() != HttpURLConnection.HTTP_NOT_FOUND) { throw e; } return null; } catch (InterruptedException ie) { Thread.currentThread().interrupt(); throw KubernetesClientException.launderThrowable(ie); } catch (IOException e) { throw KubernetesClientException.launderThrowable(e); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy