com.openshift.internal.restclient.DefaultClient Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2015 Red Hat, Inc. Distributed under license by Red Hat, Inc.
* All rights reserved. This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors: Red Hat, Inc.
******************************************************************************/
package com.openshift.internal.restclient;
import static com.openshift.internal.restclient.capability.CapabilityInitializer.initializeClientCapabilities;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.jboss.dmr.ModelNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.openshift.internal.restclient.http.HttpClientException;
import com.openshift.internal.restclient.http.UnauthorizedException;
import com.openshift.internal.restclient.http.UrlConnectionHttpClientBuilder;
import com.openshift.internal.restclient.model.Status;
import com.openshift.internal.restclient.model.properties.ResourcePropertiesRegistry;
import com.openshift.restclient.IClient;
import com.openshift.restclient.IResourceFactory;
import com.openshift.restclient.ISSLCertificateCallback;
import com.openshift.restclient.OpenShiftException;
import com.openshift.restclient.ResourceKind;
import com.openshift.restclient.UnsupportedOperationException;
import com.openshift.restclient.authorization.AuthorizationClientFactory;
import com.openshift.restclient.authorization.IAuthorizationClient;
import com.openshift.restclient.authorization.IAuthorizationContext;
import com.openshift.restclient.authorization.IAuthorizationDetails;
import com.openshift.restclient.authorization.IAuthorizationStrategy;
import com.openshift.restclient.authorization.ResourceForbiddenException;
import com.openshift.restclient.capability.CapabilityVisitor;
import com.openshift.restclient.capability.ICapability;
import com.openshift.restclient.http.IHttpClient;
import com.openshift.restclient.http.IHttpStatusCodes;
import com.openshift.restclient.model.IList;
import com.openshift.restclient.model.IResource;
import com.openshift.restclient.model.user.IUser;
/**
* @author Jeff Cantrill
*/
public class DefaultClient implements IClient, IHttpStatusCodes{
public static final String SYSTEM_PROP_K8E_API_VERSION = "osjc.k8e.apiversion";
public static final String SYSTEM_PROP_OPENSHIFT_API_VERSION = "osjc.openshift.apiversion";
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultClient.class);
private URL baseUrl;
private IHttpClient client;
private IResourceFactory factory;
private Map, ICapability> capabilities = new HashMap, ICapability>();
private boolean capabilitiesInitialized = false;
private static final String API_ENDPOINT = "api";
private static final String OS_API_LEGACY_ENDPOINT = "osapi";
private static final String OS_API_ENDPOINT = "oapi";
private final Map typeMappings = new HashMap();
private String openShiftVersion;
private String kubernetesVersion;
private IAuthorizationStrategy strategy;
private IAuthorizationClient authClient;
public DefaultClient(URL baseUrl, ISSLCertificateCallback sslCertCallback){
this(baseUrl, null, sslCertCallback);
}
/*
* Testing constructor
*/
DefaultClient(URL baseUrl, IHttpClient httpClient, ISSLCertificateCallback sslCertCallback){
this.baseUrl = baseUrl;
client = httpClient != null ? httpClient : newIHttpClient(sslCertCallback);
factory = new ResourceFactory(this);
openShiftVersion = System.getProperty(SYSTEM_PROP_OPENSHIFT_API_VERSION, null);
kubernetesVersion = System.getProperty(SYSTEM_PROP_K8E_API_VERSION, null);
authClient = new AuthorizationClientFactory().create(this);
}
/*
* Factory method for testing
*/
private IHttpClient newIHttpClient(ISSLCertificateCallback sslCertCallback){
return new UrlConnectionHttpClientBuilder()
.setAcceptMediaType("application/json")
.setSSLCertificateCallback(sslCertCallback)
.client();
}
@Override
public IResourceFactory getResourceFactory() {
return factory;
};
@Override
public String getResourceURI(IResource resource) {
return new URLBuilder(getBaseURL(), getTypeMappings(), resource).build().toString();
}
@Override
public List list(String kind) {
return list(kind,""); //assumes namespace=default
}
@Override
public List list(String kind, String namespace) {
return list(kind, namespace, new HashMap());
}
@SuppressWarnings("unchecked")
@Override
public List list(String kind, String namespace, Map labels) {
try {
if(!getTypeMappings().containsKey(kind))
// TODO: replace with specific runtime exception
throw new RuntimeException("No OpenShift resource endpoint for type: " + kind);
URLBuilder builder = new URLBuilder(this.baseUrl, getTypeMappings())
.kind(kind)
.namespace(namespace);
final URL endpoint = builder.build();
String response = client.get(endpoint, IHttpClient.DEFAULT_READ_TIMEOUT);
LOGGER.debug(String.format("List Response: %s:", response));
List items = (List) factory.createList(response, kind);
return filterItems(items, labels); //client filter until we can figure out how to restrict with a server call
} catch (HttpClientException e){
throw createOpenShiftException(String.format("Could not list %s resources in namespace %s: %s", kind, namespace, e.getMessage()), e);
} catch (SocketTimeoutException e) {
throw new OpenShiftException(e, "Socket timeout listing resources");
}
}
private List filterItems(List items, Map labels){
if(labels.isEmpty()) return items;
List filtered = new ArrayList();
for (T item : items) {
if( item.getLabels().entrySet().containsAll(labels.entrySet())){
filtered.add(item);
}
}
return filtered;
}
@Override
public Collection create(IList list, String namespace){
List results = new ArrayList(list.getItems().size());
for (IResource resource : list.getItems()) {
try{
results.add(createVersion(resource, namespace, resource.getApiVersion()));
}catch(OpenShiftException e){
if(e.getStatus() != null){
results.add(e.getStatus());
}else{
throw e;
}
}
}
return results;
}
@Override
public T create(T resource) {
return create(resource, resource.getNamespace());
}
@Override
public T create(T resource, String namespace) {
return createVersion(resource, namespace, null);
}
private T createVersion(T resource, String namespace, String version) {
if(ResourceKind.LIST.equals(resource.getKind())) throw new UnsupportedOperationException("Generic create operation not supported for resource type 'List'");
try {
namespace = ResourceKind.PROJECT.equals(resource.getKind()) ? "" : namespace;
final URL endpoint = new URLBuilder(this.baseUrl, getTypeMappings())
.kind(resource.getKind())
.namespace(namespace)
.build();
String response = client.post(endpoint, IHttpClient.DEFAULT_READ_TIMEOUT, resource);
LOGGER.debug(response);
return factory.create(response);
} catch (HttpClientException e){
throw createOpenShiftException(String.format("Could not create resource %s in namespace %s: %s", resource.getName(), namespace, e.getMessage()), e);
} catch (SocketTimeoutException e) {
throw new OpenShiftException(e, "Socket timeout creating resource %s", resource.getName());
}
}
@Override
public T create(String kind, String namespace, String name, String subresource, IResource payload) {
if(ResourceKind.LIST.equals(kind)) throw new UnsupportedOperationException("Generic create operation not supported for resource type 'List'");
try {
namespace = ResourceKind.PROJECT.equals(kind) ? "" : namespace;
final URL endpoint = new URLBuilder(this.baseUrl, getTypeMappings())
.kind(kind)
.name(name)
.namespace(namespace)
.subresource(subresource)
.build();
String response = client.post(endpoint, IHttpClient.DEFAULT_READ_TIMEOUT, payload);
LOGGER.debug(response);
return factory.create(response);
} catch (HttpClientException e){
throw createOpenShiftException(String.format("Could not create %s resource %s in namespace %s for subresource %s: %s", kind, name, namespace, subresource, e.getMessage()), e);
} catch (SocketTimeoutException e) {
throw new OpenShiftException(e, "Socket timeout creating resource %s", name);
}
}
@Override
public T update(T resource) {
if(ResourceKind.LIST.equals(resource.getKind())) throw new UnsupportedOperationException("Update operation not supported for resource type 'List'");
try {
final URL endpoint = new URLBuilder(getBaseURL(), getTypeMappings())
.resource(resource)
.namespace(resource.getNamespace())
.build();
String response = client.put(endpoint, IHttpClient.DEFAULT_READ_TIMEOUT, resource);
LOGGER.debug(response);
return factory.create(response);
} catch (HttpClientException e){
throw createOpenShiftException(String.format("Could not update resource %s: %s", resource.getName(), e.getMessage()), e);
} catch (SocketTimeoutException e) {
throw new OpenShiftException(e, "Socket timeout updating resource %s", resource.getName());
}
}
@Override
public void delete(T resource) {
if(ResourceKind.LIST.equals(resource.getKind())) throw new UnsupportedOperationException("Delete operation not supported for resource type 'List'");
try {
String namespace = ResourceKind.PROJECT.equals(resource.getKind()) ? "" : resource.getNamespace();
final URL endpoint = new URLBuilder(this.baseUrl, getTypeMappings())
.resource(resource)
.namespace(namespace)
.build();
LOGGER.debug(String.format("Deleting resource %s", endpoint));
String response = client.delete(endpoint, IHttpClient.DEFAULT_READ_TIMEOUT);
LOGGER.debug(response);
//TODO return response object here
} catch (HttpClientException e){
throw createOpenShiftException(String.format("Could not delete resource %s: %s", resource.getName(), e.getMessage()), e);
} catch (SocketTimeoutException e) {
throw new OpenShiftException(e, "SocketTimeout deleting resource %s", resource.getName());
}
}
@Override
public T get(String kind, String name, String namespace) {
try {
namespace = ResourceKind.PROJECT.equals(kind) ? "" : namespace;
final URL endpoint = new URLBuilder(this.baseUrl, getTypeMappings())
.kind(kind)
.name(name)
.namespace(namespace)
.build();
String response = client.get(endpoint, IHttpClient.DEFAULT_READ_TIMEOUT);
LOGGER.debug(response);
return factory.create(response);
} catch (HttpClientException e){
throw createOpenShiftException(String.format("Could not get resource %s in namespace %s: %s", name, namespace, e.getMessage()), e);
} catch (SocketTimeoutException e) {
throw new OpenShiftException(e, "SocketTimeout getting resource %s", name);
}
}
public synchronized void initializeCapabilities(){
if(capabilitiesInitialized) return;
initializeClientCapabilities(capabilities, this);
capabilitiesInitialized = true;
}
@SuppressWarnings("unchecked")
@Override
public T getCapability(Class capability) {
return (T) capabilities.get(capability);
}
@Override
public boolean supports(Class extends ICapability> capability) {
if(!capabilitiesInitialized ){
initializeCapabilities();
}
return capabilities.containsKey(capability);
}
@SuppressWarnings("unchecked")
@Override
public R accept(CapabilityVisitor visitor, R unsupportedCapabililityValue){
if(!capabilitiesInitialized) initializeCapabilities();
if(capabilities.containsKey(visitor.getCapabilityType())){
T capability = (T) capabilities.get(visitor.getCapabilityType());
return (R) visitor.visit(capability);
}
return unsupportedCapabililityValue;
}
public List getKubernetesVersions() {
return getVersion(KubernetesAPIVersion.class, API_ENDPOINT);
}
public List getOpenShiftVersions() {
List versions = getVersion(OpenShiftAPIVersion.class, OS_API_ENDPOINT);
versions.addAll(getVersion(OpenShiftAPIVersion.class, OS_API_LEGACY_ENDPOINT));
return versions;
}
public String getKubernetesVersion() {
if(kubernetesVersion == null){
List versions = getKubernetesVersions();
kubernetesVersion = ResourcePropertiesRegistry.getInstance().getMaxSupportedKubernetesVersion(versions).toString();
}
return kubernetesVersion;
}
@Override
public String getOpenShiftAPIVersion() {
if(openShiftVersion == null){
List versions = getOpenShiftVersions();
openShiftVersion = ResourcePropertiesRegistry.getInstance().getMaxSupportedOpenShiftVersion(versions).toString();
}
return openShiftVersion;
}
private > List getVersion(Class klass, String endpoint) {
try {
final URL url = new URL(this.baseUrl, endpoint);
LOGGER.debug(url.toString());
String response = client.get(url, IHttpClient.DEFAULT_READ_TIMEOUT);
LOGGER.debug(response);
ModelNode json = ModelNode.fromJSONString(response);
List versionNodes = json.get("versions").asList();
List versions = new ArrayList(versionNodes.size());
for (ModelNode node : versionNodes) {
try{
versions.add(Enum.valueOf(klass, node.asString()));
}catch(IllegalArgumentException e){
LOGGER.warn(String.format("Unsupported server version '%s' for '%s'", node.asString(), klass.getSimpleName()));
}
}
return versions;
} catch (MalformedURLException e) {
LOGGER.error("Exception", e);
throw new OpenShiftException(e,"");
} catch (SocketTimeoutException e) {
LOGGER.error("Exception", e);
throw new OpenShiftException(e,"");
//HACK - This gets us around a server issue
} catch (HttpClientException e) {
if(e.getResponseCode() != 403) {
throw e;
}
LOGGER.error("Unauthorized exception. Can system:anonymous get the API endpoint", e);
return new ArrayList();
}
}
private Map getTypeMappings(){
return getTypeMappings(null);
}
private Map getTypeMappings(String apiVersion){
if(typeMappings.isEmpty()){
//OpenShift endpoints
final String version = StringUtils.defaultIfEmpty(apiVersion, getOpenShiftAPIVersion());
final String osEndpoint = String.format("%s/%s", OpenShiftAPIVersion.v1beta3.toString().equals(version) ? OS_API_LEGACY_ENDPOINT : OS_API_ENDPOINT, version);
typeMappings.put(ResourceKind.BUILD, osEndpoint);
typeMappings.put(ResourceKind.BUILD_CONFIG, osEndpoint);
typeMappings.put(ResourceKind.DEPLOYMENT_CONFIG, osEndpoint);
typeMappings.put(ResourceKind.IMAGE_STREAM, osEndpoint);
typeMappings.put(ResourceKind.OAUTH_ACCESS_TOKEN, osEndpoint);
typeMappings.put(ResourceKind.OAUTH_AUTHORIZE_TOKEN, osEndpoint);
typeMappings.put(ResourceKind.OAUTH_CLIENT, osEndpoint);
typeMappings.put(ResourceKind.OAUTH_CLIENT_AUTHORIZATION, osEndpoint);
typeMappings.put(ResourceKind.POLICY, osEndpoint);
typeMappings.put(ResourceKind.POLICY_BINDING, osEndpoint);
typeMappings.put(ResourceKind.PROJECT, osEndpoint);
typeMappings.put(ResourceKind.PROJECT_REQUEST, osEndpoint);
typeMappings.put(ResourceKind.ROLE, osEndpoint);
typeMappings.put(ResourceKind.ROLE_BINDING, osEndpoint);
typeMappings.put(ResourceKind.ROUTE, osEndpoint);
typeMappings.put(ResourceKind.TEMPLATE, osEndpoint);
typeMappings.put(ResourceKind.USER, osEndpoint);
//not real kinds
typeMappings.put(ResourceKind.TEMPLATE_CONFIG, osEndpoint);
typeMappings.put(ResourceKind.PROCESSED_TEMPLATES, osEndpoint);
//Kubernetes endpoints
final String k8eApiVersion = StringUtils.defaultIfEmpty(apiVersion, getKubernetesVersion());
final String k8eEndpoint = String.format("%s/%s", API_ENDPOINT, k8eApiVersion);
typeMappings.put(ResourceKind.EVENT, k8eEndpoint);
typeMappings.put(ResourceKind.POD, k8eEndpoint);
typeMappings.put(ResourceKind.PVC, k8eEndpoint);
typeMappings.put(ResourceKind.LIMIT_RANGE, k8eEndpoint);
typeMappings.put(ResourceKind.REPLICATION_CONTROLLER, k8eEndpoint);
typeMappings.put(ResourceKind.RESOURCE_QUOTA, k8eEndpoint);
typeMappings.put(ResourceKind.SERVICE, k8eEndpoint);
typeMappings.put(ResourceKind.SECRET, k8eEndpoint);
typeMappings.put(ResourceKind.SERVICE_ACCOUNT, k8eEndpoint);
}
return typeMappings;
}
@Override
public URL getBaseURL() {
return this.baseUrl;
}
@SuppressWarnings("deprecation")
@Override
public void setAuthorizationStrategy(IAuthorizationStrategy strategy) {
this.strategy = strategy;
this.client.setAuthorizationStrategy(strategy);
}
@Override
public IAuthorizationStrategy getAuthorizationStrategy() {
return this.strategy;
}
private OpenShiftException createOpenShiftException(String message, HttpClientException e) {
LOGGER.debug(message, e);
final String token = strategy != null ? strategy.getToken() : "";
if (e.getMessage() != null
&& e.getMessage().startsWith("{")) {
Status status = factory.create(e.getMessage());
if(status.getCode() == STATUS_FORBIDDEN) {
if(StringUtils.isNotBlank(token)) { //truly forbidden
return new ResourceForbiddenException(status.getMessage(), e);
}else {
return new com.openshift.restclient.authorization.UnauthorizedException(authClient.getAuthorizationDetails(this.baseUrl.toString()));
}
}
return new OpenShiftException(e, status, message);
} else {
if(e instanceof UnauthorizedException) {
return new com.openshift.restclient.authorization.UnauthorizedException(authClient.getAuthorizationDetails(this.baseUrl.toString()));
}
return new OpenShiftException(e, message);
}
}
@Override
public IUser getCurrentUser() {
return get(ResourceKind.USER, "~", "");
}
@Override
public IAuthorizationContext getContext(String baseURL) {
return this.authClient.getContext(baseURL);
}
@Override
public IAuthorizationDetails getAuthorizationDetails(String baseURL) {
return this.authClient.getAuthorizationDetails(baseURL);
}
@Override
public void setSSLCertificateCallback(ISSLCertificateCallback callback) {
this.authClient.setSSLCertificateCallback(callback);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((baseUrl == null) ? 0 : baseUrl.hashCode());
result = prime * result + ((kubernetesVersion == null) ? 0 : kubernetesVersion.hashCode());
result = prime * result + ((openShiftVersion == null) ? 0 : openShiftVersion.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof DefaultClient))
return false;
DefaultClient other = (DefaultClient) obj;
if (baseUrl == null) {
if (other.baseUrl != null)
return false;
} else if (!baseUrl.equals(other.baseUrl))
return false;
if (kubernetesVersion == null) {
if (other.kubernetesVersion != null)
return false;
} else if (!kubernetesVersion.equals(other.kubernetesVersion))
return false;
if (openShiftVersion == null) {
if (other.openShiftVersion != null)
return false;
} else if (!openShiftVersion.equals(other.openShiftVersion))
return false;
return true;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy