
io.fabric8.kubernetes.client.utils.BackwardsCompatibilityInterceptor 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.utils;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.client.dsl.base.OperationSupport;
import io.fabric8.kubernetes.client.http.BasicBuilder;
import io.fabric8.kubernetes.client.http.HttpRequest;
import io.fabric8.kubernetes.client.http.HttpResponse;
import io.fabric8.kubernetes.client.http.Interceptor;
import io.fabric8.kubernetes.client.http.HttpRequest.Builder;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class BackwardsCompatibilityInterceptor implements Interceptor {
public static final String NAME = "BACKWARDS";
private static final int API_GROUP = 1;
private static final int API_VERSION = 2;
private static final int PATH = 3;
private static final String PATCH = "patch";
private static final String NAME_REGEX = "[a-z0-9\\-\\.]+";
private static final Pattern URL_PATTERN = Pattern.compile("[^ ]+/apis/(" + NAME_REGEX + ")/(" + NAME_REGEX + ")/(" + NAME_REGEX + ")[^ ]*");
private static final Pattern NAMESPACED_URL_PATTERN = Pattern.compile("[^ ]+/apis/(" + NAME_REGEX + ")/(" + NAME_REGEX + ")/namespaces/" + NAME_REGEX + "/(" + NAME_REGEX + ")[^ ]*");
private static final Map notFoundTransformations = new HashMap<>();
private static final Map badRequestTransformations = new HashMap<>();
private static final Map openshiftOAPITransformations = new HashMap<>();
private static final Map> responseCodeToTransformations = new HashMap<>();
private static class ResourceKey {
private final String kind;
private final String path;
private final String group;
private final String version;
public ResourceKey(String kind, String path, String group, String version) {
this.kind = kind;
this.path = path;
this.group = group;
this.version = version;
}
public String getKind() {
return kind;
}
public String getPath() {
return path;
}
public String getGroup() {
return group;
}
public String getVersion() {
return version;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ResourceKey key = (ResourceKey) o;
return Objects.equals(path, key.path) &&
Objects.equals(group, key.group) &&
Objects.equals(version, key.version);
}
@Override
public int hashCode() {
return Objects.hash(path, group, version);
}
}
static {
notFoundTransformations.put(new ResourceKey("Deployment", "deployments", "apps", "v1"), new ResourceKey("Deployment", "deployments", "extensions", "v1beta1"));
notFoundTransformations.put(new ResourceKey("StatefulSet", "statefulsets", "apps", "v1"), new ResourceKey("StatefulSet", "statefulsets", "apps", "v1beta1"));
notFoundTransformations.put(new ResourceKey("DaemonSets", "daemonsets", "apps", "v1"), new ResourceKey("DaemonSet", "daemonsets", "extensions", "v1beta1"));
notFoundTransformations.put(new ResourceKey("ReplicaSets", "replicasets", "apps", "v1"), new ResourceKey("ReplicaSet", "replicasets", "extensions", "v1beta1"));
notFoundTransformations
.put(new ResourceKey("NetworkPolicy", "networkpolicies", "networking.k8s.io", "v1"), new ResourceKey("NetworkPolicy", "networkpolicies", "extensions", "v1beta1"));
notFoundTransformations
.put(new ResourceKey("StorageClass", "storageclasses", "storage.k8s.io", "v1"), new ResourceKey("StorageClass", "storageclasses", "extensions", "v1beta1"));
notFoundTransformations.put(new ResourceKey("RoleBinding", "rolebindings", "rbac.authorization.k8s.io", "v1"), new ResourceKey("RoleBinding", "rolebindings", "rbac.authorization.k8s.io", "v1beta1"));
notFoundTransformations.put(new ResourceKey("Role", "roles", "rbac.authorization.k8s.io", "v1"), new ResourceKey("Role", "roles", "rbac.authorization.k8s.io", "v1beta1"));
notFoundTransformations.put(new ResourceKey("ClusterRoleBinding", "clusterrolebindings", "rbac.authorization.k8s.io", "v1"), new ResourceKey("ClusterRoleBinding", "clusterrolebindings", "rbac.authorization.k8s.io", "v1beta1"));
notFoundTransformations.put(new ResourceKey("ClusterRole", "clusterroles", "rbac.authorization.k8s.io", "v1"), new ResourceKey("ClusterRole", "clusterroles", "rbac.authorization.k8s.io", "v1beta1"));
notFoundTransformations.put(new ResourceKey("CronJob", "cronjobs", "batch", "v1beta1"), new ResourceKey("CronJob", "cronjob", "batch", "v2alpha1"));
notFoundTransformations.put(new ResourceKey("Template", "template", "", "v1"), new ResourceKey("Template", "template", "template.openshift.io", "v1"));
badRequestTransformations.put(new ResourceKey("Deployment", "deployments", "apps", "v1beta1"), new ResourceKey("Deployment", "deployments", "extensions", "v1beta1"));
responseCodeToTransformations.put(400, badRequestTransformations);
responseCodeToTransformations.put(404, notFoundTransformations);
/**
* OpenShift versions prior to 3.10 use the /oapi endpoint for Openshift specific resources.
* However, since 3.10 /apis/{group} is being used. This has been removed completely in 4.x
* versions of OpenShift. Hence, this code is to handle those cases.
*/
openshiftOAPITransformations.put("routes", new ResourceKey("Route", "routes", "route.openshift.io", "v1"));
openshiftOAPITransformations.put("templates", new ResourceKey("Template", "templates", "template.openshift.io", "v1"));
openshiftOAPITransformations.put("buildconfigs", new ResourceKey("BuildConfig", "buildconfigs", "build.openshift.io", "v1"));
openshiftOAPITransformations.put("deploymentconfigs", new ResourceKey("DeploymentConfig", "deploymentconfigs", "apps.openshift.io", "v1"));
openshiftOAPITransformations.put("imagestreams", new ResourceKey("ImageStream", "imagestreams", "image.openshift.io", "v1"));
openshiftOAPITransformations.put("imagestreamtags", new ResourceKey("ImageStream", "imagestreamtags", "image.openshift.io", "v1"));
openshiftOAPITransformations.put("securitycontextconstraints", new ResourceKey("SecurityContextConstraints", "securitycontextconstraints", "security.openshift.io", "v1"));
}
@Override
public boolean afterFailure(Builder builder, HttpResponse> response) {
ResourceKey target = findNewTarget(builder, response);
if (target == null) {
return false;
}
HttpRequest request = response.request();
if (request.bodyString() != null && !request.method().equalsIgnoreCase(PATCH)) {
Object object = Serialization.unmarshal(request.bodyString());
if (object instanceof HasMetadata) {
HasMetadata h = (HasMetadata) object;
h.setApiVersion(target.group + "/" + target.version);
switch (request.method()) {
case "POST":
builder.post(OperationSupport.JSON, Serialization.asJson(h));
break;
case "PUT":
builder.put(OperationSupport.JSON, Serialization.asJson(h));
break;
case "DELETE":
builder.delete(OperationSupport.JSON, Serialization.asJson(h));
break;
default:
return false;
}
}
}
return true;
}
public ResourceKey findNewTarget(BasicBuilder basicBuilder, HttpResponse> response) {
HttpRequest request = response.request();
if (isDeprecatedOpenshiftOapiRequest(request)) {
return handleOpenshiftOapiRequests(basicBuilder, request, response);
} else if (!response.isSuccessful() && responseCodeToTransformations.keySet().contains(response.code())) {
String url = request.uri().toString();
Matcher matcher = getMatcher(url);
ResourceKey key = getKey(matcher);
ResourceKey target = responseCodeToTransformations.get(response.code()).get(key);
if (target != null) {
String newUrl = new StringBuilder(url)
.replace(matcher.start(API_VERSION), matcher.end(API_VERSION), target.version) // Order matters: We need to substitute right to left, so that former substitution don't affect the indexes of later.
.replace(matcher.start(API_GROUP), matcher.end(API_GROUP), target.group)
.toString();
basicBuilder.uri(URI.create(newUrl));
return target;
}
}
return null;
}
@Override
public boolean afterFailure(BasicBuilder basicBuilder, HttpResponse> response) {
return findNewTarget(basicBuilder, response) != null;
}
private static Matcher getMatcher(String url) {
Matcher m = NAMESPACED_URL_PATTERN.matcher(url);
if (m.matches()) {
return m;
}
m = URL_PATTERN.matcher(url);
if (m.matches()) {
return m;
}
return null;
}
private static ResourceKey getKey(Matcher m) {
return m != null ? new ResourceKey(null, m.group(PATH), m.group(API_GROUP), m.group(API_VERSION)) : null;
}
private static ResourceKey handleOpenshiftOapiRequests(BasicBuilder builder, HttpRequest request, HttpResponse> response) {
if (!response.isSuccessful() && response.code() == HttpURLConnection.HTTP_NOT_FOUND) {
String requestUrl = request.uri().toString();
// handle case when /oapi is not available
String[] parts = requestUrl.split("/");
String resourcePath = parts[parts.length - 1];
ResourceKey target = openshiftOAPITransformations.get(resourcePath);
if (target != null) {
requestUrl = requestUrl.replace("/oapi", "/apis/" + target.getGroup());
builder.uri(URI.create(requestUrl));
return target;
}
}
return null;
}
private static boolean isDeprecatedOpenshiftOapiRequest(HttpRequest request) {
return request.uri().toString().contains("oapi");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy