Skip to content

Commit

Permalink
Fix #1804: Unable to create Template on OCP4
Browse files Browse the repository at this point in the history
Modified BackwardCompatibilityInterceptor for handling /oapi
failures in case of OpenShift
  • Loading branch information
rohanKanojia committed Jan 16, 2020
1 parent 403a6c5 commit 68cc1d6
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
### 4.7-SNAPSHOT
#### Bugs
* `Quantity.getAmountInBytes()` should be able to handle negative exponents
* Fix #1805: Unable to create Template on OCP4

#### Improvements

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class BackwardsCompatibilityInterceptor implements Interceptor {
private static final Pattern NAMESPACED_URL_PATTERN = Pattern.compile("[^ ]+/apis/(" + NAME_REGEX + ")/(" + NAME_REGEX + ")/namespaces/" + NAME_REGEX + "/(" + NAME_REGEX + ")[^ ]*");
private static final Map<ResourceKey, ResourceKey> notFoundTransformations = new HashMap<>();
private static final Map<ResourceKey, ResourceKey> badRequestTransformations = new HashMap<>();
private static final Map<String, ResourceKey> openshiftOAPITransformations = new HashMap<>();

private static final Map<Integer, Map<ResourceKey, ResourceKey>> responseCodeToTransformations = new HashMap<>();

Expand Down Expand Up @@ -107,17 +108,33 @@ public int hashCode() {
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"));
}

public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
if (!response.isSuccessful() && responseCodeToTransformations.keySet().contains(response.code())) {
if (isDeprecatedOpenshiftOapiRequest(request)) {
return handleOpenshiftOapiRequests(request, response, chain);
} else if (!response.isSuccessful() && responseCodeToTransformations.keySet().contains(response.code())) {
String url = request.url().toString();
Matcher matcher = getMatcher(url);
ResourceKey key = getKey(matcher);
Expand All @@ -129,21 +146,7 @@ public Response intercept(Chain chain) throws IOException {
.replace(matcher.start(API_GROUP), matcher.end(API_GROUP), target.group)
.toString();

Request.Builder newRequest = request.newBuilder()
.url(newUrl);

Buffer buffer = new Buffer();
if (request.body() != null && !request.method().equalsIgnoreCase(PATCH)) {
request.body().writeTo(buffer);
Object object = Serialization.unmarshal(buffer.inputStream());
if (object instanceof HasMetadata) {
HasMetadata h = (HasMetadata) object;
h.setApiVersion(target.group + "/" + target.version);
newRequest = newRequest.method(request.method(), RequestBody.create(OperationSupport.JSON, Serialization.asJson(h)));
}
}

return chain.proceed(newRequest.build());
return handleNewRequestAndProceed(request, newUrl, target, chain);
}
}
return response;
Expand All @@ -165,4 +168,48 @@ private static Matcher getMatcher(String url) {
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 Response handleOpenshiftOapiRequests(Request request, Response response, Chain chain) throws IOException{
if (!response.isSuccessful()) {
String requestUrl = request.url().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());
return handleNewRequestAndProceed(request, requestUrl, target, chain);
}
}
return response;
}

private static Response handleNewRequestAndProceed(Request request, String newUrl, ResourceKey target, Chain chain) throws IOException {
Request.Builder newRequest = request.newBuilder()
.url(newUrl);

if (request.body() != null && !request.method().equalsIgnoreCase(PATCH)) {
try (Buffer buffer = new Buffer()) {
request.body().writeTo(buffer);

Object object = Serialization.unmarshal(buffer.inputStream());
if (object instanceof HasMetadata) {
HasMetadata h = (HasMetadata) object;
if (target != null) {
h.setApiVersion(target.group + "/" + target.version);
}
newRequest = newRequest.method(request.method(), RequestBody.create(OperationSupport.JSON, Serialization.asJson(h)));
}
}
}

return chain.proceed(newRequest.build());
}

private static boolean isDeprecatedOpenshiftOapiRequest(Request request) {
if (request != null && request.url() != null) {
return request.url().toString().contains("oapi");
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public void init() {

template1 = new TemplateBuilder()
.withApiVersion("template.openshift.io/v1")
.withNewMetadata().withName("foo").endMetadata()
.withNewMetadata().withName("foo").addToLabels("group", "fabric8").endMetadata()
.addToObjects(aService)
.build();

Expand Down Expand Up @@ -109,10 +109,22 @@ public void delete() {
assertTrue(bDeleted);
}

@Test
public void testOCP4Compatibility() {
Template template = client.templates().load(getClass().getResourceAsStream("/test-template.yml")).get();
// Set ApiVersion to v1 so that client uses old /oapi endpoints
template.setApiVersion("v1");
template.getMetadata().setLabels(Collections.singletonMap("group", "fabric8"));

Template templateAsResponse = client.templates().inNamespace(currentNamespace).create(template);
assertEquals("template.openshift.io/v1", templateAsResponse.getApiVersion());
assertEquals("redis-template", templateAsResponse.getMetadata().getName());
}

@After
public void cleanup() throws InterruptedException {
if (client.templates().inNamespace(currentNamespace).list().getItems().size()!= 0) {
client.templates().inNamespace(currentNamespace).withName("foo").delete();
client.templates().inNamespace(currentNamespace).withLabels(Collections.singletonMap("group", "fabric8")).delete();
}

DeleteEntity<Template> templateDelete = new DeleteEntity<>(Template.class, client, "foo", currentNamespace);
Expand Down

0 comments on commit 68cc1d6

Please sign in to comment.