/*
* Copyright [2013] [Cloud4SOA, www.cloud4soa.eu]
*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*
* Copyright 2009-2012 the original author or authors.
*
* 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 org.cloudfoundry.client.lib.rest;
import org.cloudfoundry.client.lib.CloudCredentials;
import org.cloudfoundry.client.lib.CloudFoundryException;
import org.cloudfoundry.client.lib.HttpProxyConfiguration;
import org.cloudfoundry.client.lib.UploadStatusCallback;
import org.cloudfoundry.client.lib.archive.ApplicationArchive;
import org.cloudfoundry.client.lib.archive.DirectoryApplicationArchive;
import org.cloudfoundry.client.lib.archive.ZipApplicationArchive;
import org.cloudfoundry.client.lib.domain.ApplicationStats;
import org.cloudfoundry.client.lib.domain.CloudApplication;
import org.cloudfoundry.client.lib.domain.CloudInfo;
import org.cloudfoundry.client.lib.domain.CloudResource;
import org.cloudfoundry.client.lib.domain.CloudResources;
import org.cloudfoundry.client.lib.domain.CloudService;
import org.cloudfoundry.client.lib.domain.CloudServiceOffering;
import org.cloudfoundry.client.lib.domain.CloudServicePlan;
import org.cloudfoundry.client.lib.domain.CloudSpace;
import org.cloudfoundry.client.lib.domain.CrashesInfo;
import org.cloudfoundry.client.lib.domain.InstanceState;
import org.cloudfoundry.client.lib.domain.InstanceStats;
import org.cloudfoundry.client.lib.domain.InstancesInfo;
import org.cloudfoundry.client.lib.domain.ServiceConfiguration;
import org.cloudfoundry.client.lib.domain.Staging;
import org.cloudfoundry.client.lib.domain.UploadApplicationPayload;
import org.cloudfoundry.client.lib.oauth2.OauthClient;
import org.cloudfoundry.client.lib.util.CloudEntityResourceMapper;
import org.cloudfoundry.client.lib.util.CloudUtil;
import org.cloudfoundry.client.lib.util.JsonUtil;
import org.cloudfoundry.client.lib.util.RestUtil;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpClientErrorException;
import java.io.File;
import java.io.IOException;
import java.net.URI;
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 java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipFile;
/**
* Empty implementation for cloud controller v2 REST API
*
* @author Thomas Risberg
*/
public class CloudControllerClientV2 extends AbstractCloudControllerClient {
// This map only contains framework/runtime mapping for frameworks that we actively support
private static Map<String, String[]> FRAMEWORK_RUNTIME_PATTERNS = new HashMap<String, String[]>() {{
put("spring", new String[] {"java.*"});
put("lift", new String[] {"java.*"});
put("grails", new String[] {"java.*"});
put("java_web", new String[] {"java.*"});
}};
private OauthClient oauthClient;
private CloudSpace sessionSpace;
private CloudEntityResourceMapper resourceMapper = new CloudEntityResourceMapper();
private Map<String, UUID> runtimeIdCache = new HashMap<String, UUID>();
private Map<String, UUID> frameworkIdCache = new HashMap<String, UUID>();
public CloudControllerClientV2(URL cloudControllerUrl,
RestUtil restUtil,
CloudCredentials cloudCredentials,
URL authorizationEndpoint,
CloudSpace sessionSpace,
HttpProxyConfiguration httpProxyConfiguration) {
super(cloudControllerUrl, restUtil, cloudCredentials, authorizationEndpoint, httpProxyConfiguration);
this.oauthClient = restUtil.createOauthClient(authorizationEndpoint, httpProxyConfiguration);
this.sessionSpace = sessionSpace;
}
@SuppressWarnings("unchecked")
public CloudInfo getInfo() {
String infoJson = getRestTemplate().getForObject(getUrl("/v2/info"), String.class);
Map<String, Object> infoMap = JsonUtil.convertJsonToMap(infoJson);
Map<String, Object> userMap = getUserInfo((String) infoMap.get("user"));
//TODO: replace with v2 api call once, or if, they become available
String infoV1Json = getRestTemplate().getForObject(getUrl("/info"), String.class);
Map<String, Object> infoV1Map = JsonUtil.convertJsonToMap(infoV1Json);
Map<String, Object> limitMap = (Map<String, Object>) infoV1Map.get("limits");
Map<String, Object> usageMap = (Map<String, Object>) infoV1Map.get("usage");
String name = CloudUtil.parse(String.class, infoMap.get("name"));
String support = CloudUtil.parse(String.class, infoMap.get("support"));
String authorizationEndpoint = CloudUtil.parse(String.class, infoMap.get("authorization_endpoint"));
int build = CloudUtil.parse(Integer.class, infoMap.get("build"));
String version = "" + CloudUtil.parse(Number.class, infoMap.get("version"));
String description = CloudUtil.parse(String.class, infoMap.get("description"));
CloudInfo.Limits limits = null;
CloudInfo.Usage usage = null;
boolean debug = false;
Map<String, CloudInfo.Runtime> runtimes = null;
Collection<CloudInfo.Framework> frameworks = null;
if (token != null) {
limits = new CloudInfo.Limits(limitMap);
usage = new CloudInfo.Usage(usageMap);
debug = CloudUtil.parse(Boolean.class, infoV1Map.get("allow_debug"));
runtimes = getInfoForRuntimes();
frameworks = getInfoForFrameworks(runtimes);
}
return new CloudInfo(name, support, authorizationEndpoint, build, version, (String)userMap.get("user_name"),
description, limits, usage, debug, frameworks, runtimes);
}
public boolean supportsSpaces() {
return true;
}
@Override
@SuppressWarnings("unchecked")
public List<CloudSpace> getSpaces() {
String urlPath = "/v2/spaces?inline-relations-depth=1";
List<Map<String, Object>> resourceList = getAllResources(urlPath, null);
List<CloudSpace> spaces = new ArrayList<CloudSpace>();
for (Map<String, Object> resource : resourceList) {
spaces.add(resourceMapper.mapResource(resource, CloudSpace.class));
}
return spaces;
}
public String login() {
OAuth2AccessToken token = oauthClient.getToken(cloudCredentials.getEmail(),
cloudCredentials.getPassword());
this.token = token.getTokenType() + " " + token.getValue();
return this.token;
}
public void logout() {
token = null;
}
public void register(String email, String password) {
throw new UnsupportedOperationException("Feature is not yet implemented.");
}
public void updatePassword(CloudCredentials credentials, String newPassword) {
oauthClient.changePassword(token, credentials.getPassword(), newPassword);
CloudCredentials newCloudCredentials = new CloudCredentials(credentials.getEmail(), newPassword);
if (cloudCredentials.getProxyUser() != null) {
cloudCredentials = newCloudCredentials.proxyForUser(cloudCredentials.getProxyUser());
} else {
cloudCredentials = newCloudCredentials;
}
}
public void unregister() {
throw new UnsupportedOperationException("Feature is not yet implemented.");
}
@SuppressWarnings("unchecked")
public List<CloudService> getServices() {
Map<String, Object> urlVars = new HashMap<String, Object>();
String urlPath = "/v2";
if (sessionSpace != null) {
urlVars.put("space", sessionSpace.getMeta().getGuid());
urlPath = urlPath + "/spaces/{space}";
}
urlPath = urlPath + "/service_instances?inline-relations-depth={depth}";
urlVars.put("depth", 2);
List<Map<String, Object>> resourceList = getAllResources(urlPath, urlVars);
List<CloudService> services = new ArrayList<CloudService>();
for (Map<String, Object> resource : resourceList) {
services.add(resourceMapper.mapResource(resource, CloudService.class));
}
return services;
}
public void createService(CloudService service) {
Assert.notNull(sessionSpace, "Unable to create service without specifying space to use.");
Assert.notNull(service, "Service must not be null");
Assert.notNull(service.getName(), "Service name must not be null");
Assert.notNull(service.getLabel(), "Service label must not be null");
// until we have defaults - the version and plan are required
Assert.notNull(service.getVersion(), "Service version must not be null");
Assert.notNull(service.getPlan(), "Service plan must not be null");
List<CloudServiceOffering> offerings = getServiceOfferings(service.getLabel());
CloudServicePlan cloudServicePlan = null;
for (CloudServiceOffering offering : offerings) {
if (service.getVersion() != null || service.getVersion().equals(offering.getVersion())) {
for (CloudServicePlan plan : offering.getCloudServicePlans()) {
if (service.getPlan() != null && service.getPlan().equals(plan.getName())) {
cloudServicePlan = plan;
break;
}
}
}
if (cloudServicePlan != null) {
break;
}
}
Assert.notNull(cloudServicePlan, "Service Plan not found.");
HashMap<String, Object> serviceRequest = new HashMap<String, Object>();
serviceRequest.put("space_guid", sessionSpace.getMeta().getGuid());
serviceRequest.put("name", service.getName());
serviceRequest.put("service_plan_guid", cloudServicePlan.getMeta().getGuid());
getRestTemplate().postForObject(getUrl("/v2/service_instances"), serviceRequest, String.class);
}
@SuppressWarnings("unchecked")
public CloudService getService(String serviceName) {
String urlPath = "/v2";
Map<String, Object> urlVars = new HashMap<String, Object>();
if (sessionSpace != null) {
urlVars.put("space", sessionSpace.getMeta().getGuid());
urlPath = urlPath + "/spaces/{space}";
}
urlVars.put("q", "name:" + serviceName);
urlPath = urlPath + "/service_instances?inline-relations-depth=2&q={q}";
List<Map<String, Object>> resourceList = getAllResources(urlPath, urlVars);
CloudService cloudService = null;
if (resourceList.size() > 0) {
cloudService = resourceMapper.mapResource(resourceList.get(0), CloudService.class);
}
return cloudService;
}
public void deleteService(String serviceName) {
CloudService cloudService = getService(serviceName);
doDeleteService(cloudService);
}
public void deleteAllServices() {
List<CloudService> cloudServices = getServices();
for (CloudService cloudService : cloudServices) {
doDeleteService(cloudService);
}
}
@SuppressWarnings("unchecked")
public List<ServiceConfiguration> getServiceConfigurations() {
String urlPath = "/v2/services?inline-relations-depth=1";
List<Map<String, Object>> resourceList = getAllResources(urlPath, null);
List<ServiceConfiguration> serviceConfigurations = new ArrayList<ServiceConfiguration>();
for (Map<String, Object> resource : resourceList) {
CloudServiceOffering serviceOffering = resourceMapper.mapResource(resource, CloudServiceOffering.class);
serviceConfigurations.add(new ServiceConfiguration(serviceOffering));
}
return serviceConfigurations;
}
@SuppressWarnings("unchecked")
public List<CloudApplication> getApplications() {
Map<String, Object> urlVars = new HashMap<String, Object>();
String urlPath = "/v2";
if (sessionSpace != null) {
urlVars.put("space", sessionSpace.getMeta().getGuid());
urlPath = urlPath + "/spaces/{space}";
}
urlPath = urlPath + "/apps?inline-relations-depth={depth}";
urlVars.put("depth", 2);
List<Map<String, Object>> resourceList = getAllResources(urlPath, urlVars);
List<CloudApplication> apps = new ArrayList<CloudApplication>();
for (Map<String, Object> resource : resourceList) {
apps.add(mapCloudApplication(resource));
}
return apps;
}
@SuppressWarnings("unchecked")
public CloudApplication getApplication(String appName) {
Map<String, Object> resource = findApplicationResource(appName, 2);
if (resource == null) {
throw new CloudFoundryException(HttpStatus.NOT_FOUND, "Not Found", "Application not found");
}
CloudApplication cloudApp = mapCloudApplication(resource);
return cloudApp;
}
private CloudApplication mapCloudApplication(Map<String, Object> resource) {
UUID appId = resourceMapper.getGuidOfResource(resource);
CloudApplication cloudApp = null;
if (resource != null) {
int running = getRunningInstances(appId,
CloudApplication.AppState.valueOf(
CloudEntityResourceMapper.getEntityAttribute(resource, "state", String.class)));
((Map<String, Object>)resource.get("entity")).put("running_instances", running);
cloudApp = resourceMapper.mapResource(resource, CloudApplication.class);
cloudApp.setUris(findApplicationUris(cloudApp.getMeta().getGuid()));
}
return cloudApp;
}
private int getRunningInstances(UUID appId, CloudApplication.AppState appState) {
int running = 0;
ApplicationStats appStats = doGetApplicationStats(appId, appState);
if (appStats != null && appStats.getRecords() != null) {
for (InstanceStats inst : appStats.getRecords()) {
if (InstanceState.RUNNING == inst.getState()){
running++;
}
}
}
return running;
}
public ApplicationStats getApplicationStats(String appName) {
UUID appId = getAppId(appName);
CloudApplication app = getApplication(appName);
return doGetApplicationStats(appId, app.getState());
}
private ApplicationStats doGetApplicationStats(UUID appId, CloudApplication.AppState appState) {
List<InstanceStats> instanceList = new ArrayList<InstanceStats>();
if (appState.equals(CloudApplication.AppState.STARTED)) {
Map<String, Object> respMap = getInstanceInfoForApp(appId, "stats");
for (String instanceId : respMap.keySet()) {
InstanceStats instanceStats =
new InstanceStats(instanceId, (Map<String, Object>) respMap.get(instanceId));
instanceList.add(instanceStats);
}
}
return new ApplicationStats(instanceList);
}
private Map<String, Object> getInstanceInfoForApp(UUID appId, String path) {
String url = getUrl("/v2/apps/{guid}/" + path);
Map<String, Object> urlVars = new HashMap<String, Object>();
urlVars.put("guid", appId);
String resp = getRestTemplate().getForObject(url, String.class, urlVars);
Map<String, Object> respMap = JsonUtil.convertJsonToMap(resp);
return respMap;
}
public void createApplication(String appName, Staging staging, int memory, List<String> uris,
List<String> serviceNames, boolean checkExists) {
if (checkExists) {
try {
getAppId(appName);
return;
} catch (HttpClientErrorException e) {
if (e.getStatusCode() != HttpStatus.NOT_FOUND) {
throw e;
}
}
}
HashMap<String, Object> appRequest = new HashMap<String, Object>();
appRequest.put("space_guid", sessionSpace.getMeta().getGuid());
appRequest.put("name", appName);
appRequest.put("framework_guid", getFrameworkId(staging.getFramework()));
appRequest.put("runtime_guid", getRuntimeId(staging.getRuntime()));
appRequest.put("memory", memory);
appRequest.put("instances", 1);
if (staging.getCommand() != null) {
appRequest.put("command", staging.getCommand());
}
appRequest.put("state", CloudApplication.AppState.STOPPED);
String appResp = getRestTemplate().postForObject(getUrl("/v2/apps"), appRequest, String.class);
Map<String, Object> appEntity = JsonUtil.convertJsonToMap(appResp);
UUID newAppGuid = CloudEntityResourceMapper.getMeta(appEntity).getGuid();
if (serviceNames != null && serviceNames.size() > 0) {
updateApplicationServices(appName, serviceNames);
}
if (uris != null && uris.size() > 0) {
addUris(uris, newAppGuid);
}
}
private List<Map<String, Object>> getAllResources(String urlPath, Map<String, Object> urlVars) {
List<Map<String, Object>> allResources = new ArrayList<Map<String, Object>>();
String resp;
if (urlVars != null) {
resp = getRestTemplate().getForObject(getUrl(urlPath), String.class, urlVars);
} else {
resp = getRestTemplate().getForObject(getUrl(urlPath), String.class);
}
Map<String, Object> respMap = JsonUtil.convertJsonToMap(resp);
List<Map<String, Object>> newResources = (List<Map<String, Object>>) respMap.get("resources");
if (newResources != null && newResources.size() > 0) {
allResources.addAll(newResources);
for (Map<String, Object> res : newResources) {
Map<String, Object> ent = (Map<String, Object>) res.get("entity");
}
}
String nextUrl = (String) respMap.get("next_url");
while (nextUrl != null && nextUrl.length() > 0) {
nextUrl = addPageOfResources(nextUrl, allResources);
}
return allResources;
}
private String addPageOfResources(String nextUrl, List<Map<String, Object>> allResources) {
String resp = getRestTemplate().getForObject(getUrl(nextUrl), String.class);
Map<String, Object> respMap = JsonUtil.convertJsonToMap(resp);
List<Map<String, Object>> newResources = (List<Map<String, Object>>) respMap.get("resources");
if (newResources != null && newResources.size() > 0) {
allResources.addAll(newResources);
for (Map<String, Object> res : newResources) {
Map<String, Object> ent = (Map<String, Object>) res.get("entity");
}
}
return (String) respMap.get("next_url");
}
private void addUris(List<String> uris, UUID appGuid) {
Map<String, UUID> domains = getDomains();
for (String uri : uris) {
UUID domainGuid = null;
Map<String, String> uriInfo = new HashMap<String, String>(2);
extractUriInfo(domains, uri, uriInfo);
domainGuid = domains.get(uriInfo.get("domainName"));
bindRoute(uriInfo.get("host"), domainGuid, appGuid);
}
}
private void removeUris(List<String> uris, UUID appGuid) {
Map<String, UUID> domains = getDomains();
for (String uri : uris) {
UUID domainGuid = null;
Map<String, String> uriInfo = new HashMap<String, String>(2);
extractUriInfo(domains, uri, uriInfo);
domainGuid = domains.get(uriInfo.get("domainName"));
unbindRoute(uriInfo.get("host"), domainGuid, appGuid);
}
}
private void extractUriInfo(Map<String, UUID> domains, String uri, Map<String, String> uriInfo) {
URI newUri = URI.create(uri);
String authority = newUri.getScheme() != null ? newUri.getAuthority(): newUri.getPath();
for (String domain : domains.keySet()) {
if (authority != null && authority.endsWith(domain)) {
uriInfo.put("domainName", domain);
if (domain.length() < authority.length()) {
uriInfo.put("host", authority.substring(0, authority.indexOf(domain) - 1));
}
break;
}
}
if (uriInfo.get("domainName") == null) {
throw new IllegalArgumentException("Domain not found for URI " + uri);
}
if (uriInfo.get("host") == null) {
throw new IllegalArgumentException("Invalid URI " + uri +
" -- host not specified for domain " + uriInfo.get("domainName"));
}
}
private Map<String, UUID> getDomains() {
Map<String, Object> urlVars = new HashMap<String, Object>();
String urlPath = "/v2";
if (sessionSpace != null) {
urlVars.put("space", sessionSpace.getMeta().getGuid());
urlPath = urlPath + "/spaces/{space}";
}
String domainPath = urlPath + "/domains?inline-relations-depth={depth}";
urlVars.put("depth", 1);
List<Map<String, Object>> resourceList = getAllResources(domainPath, urlVars);
Map<String, UUID> domains = new HashMap<String, UUID>(resourceList.size());
for (Map<String, Object> d : resourceList) {
domains.put(
CloudEntityResourceMapper.getEntityAttribute(d, "name", String.class),
CloudEntityResourceMapper.getMeta(d).getGuid());
}
return domains;
}
private void bindRoute(String host, UUID domainGuid, UUID appGuid) {
UUID routeGuid = getRouteGuid(host);
if (routeGuid == null) {
routeGuid = addRoute(host, domainGuid);
}
String bindPath = "/v2/apps/{app}/routes/{route}";
Map<String, Object> bindVars = new HashMap<String, Object>();
bindVars.put("app", appGuid);
bindVars.put("route", routeGuid);
HashMap<String, Object> bindRequest = new HashMap<String, Object>();
getRestTemplate().put(getUrl(bindPath), bindRequest, bindVars);
}
private void unbindRoute(String host, UUID domainGuid, UUID appGuid) {
UUID routeGuid = getRouteGuid(host);
if (routeGuid != null) {
String bindPath = "/v2/apps/{app}/routes/{route}";
Map<String, Object> bindVars = new HashMap<String, Object>();
bindVars.put("app", appGuid);
bindVars.put("route", routeGuid);
getRestTemplate().delete(getUrl(bindPath), bindVars);
}
}
private UUID getRouteGuid(String host) {
Map<String, Object> urlVars = new HashMap<String, Object>();
String routePath = "/v2/routes?inline-relations-depth={depth}&q=host:{host}";
urlVars.put("depth", 0);
urlVars.put("host", host);
List<Map<String, Object>> routes = getAllResources(routePath, urlVars);
UUID routeGuid = null;
if (routes.size() > 0) {
Map<String, Object> r = routes.get(0);
routeGuid = CloudEntityResourceMapper.getMeta(r).getGuid();
}
return routeGuid;
}
private UUID addRoute(String host, UUID domainGuid) {
Assert.notNull(sessionSpace, "Unable to add route without specifying space to use.");
HashMap<String, Object> routeRequest = new HashMap<String, Object>();
routeRequest.put("host", host);
routeRequest.put("domain_guid", domainGuid);
routeRequest.put("organization_guid", sessionSpace.getOrganization().getMeta().getGuid());
String routeResp = getRestTemplate().postForObject(getUrl("/v2/routes"), routeRequest, String.class);
Map<String, Object> routeEntity = JsonUtil.convertJsonToMap(routeResp);
UUID newRouteGuid = CloudEntityResourceMapper.getMeta(routeEntity).getGuid();
return newRouteGuid;
}
public void uploadApplication(String appName, File file, UploadStatusCallback callback) throws IOException {
Assert.notNull(file, "File must not be null");
if (file.isDirectory()) {
ApplicationArchive archive = new DirectoryApplicationArchive(file);
uploadApplication(appName, archive, callback);
} else {
ZipFile zipFile = new ZipFile(file);
try {
ApplicationArchive archive = new ZipApplicationArchive(zipFile);
uploadApplication(appName, archive, callback);
} finally {
zipFile.close();
}
}
}
public void uploadApplication(String appName, ApplicationArchive archive, UploadStatusCallback callback)
throws IOException {
Assert.notNull(appName, "AppName must not be null");
Assert.notNull(archive, "Archive must not be null");
UUID appId = getAppId(appName);
if (callback == null) {
callback = UploadStatusCallback.NONE;
}
CloudResources knownRemoteResources = getKnownRemoteResources(archive);
callback.onCheckResources();
callback.onMatchedFileNames(knownRemoteResources.getFilenames());
UploadApplicationPayload payload = new UploadApplicationPayload(archive, knownRemoteResources);
callback.onProcessMatchedResources(payload.getTotalUncompressedSize());
HttpEntity<?> entity = generatePartialResourceRequest(payload, knownRemoteResources);
String url = getUrl("/v2/apps/{guid}/bits");
getRestTemplate().put(url, entity, appId);
}
private CloudResources getKnownRemoteResources(ApplicationArchive archive) throws IOException {
CloudResources archiveResources = new CloudResources(archive);
String json = JsonUtil.convertToJson(archiveResources);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(JSON_MEDIA_TYPE);
HttpEntity<String> requestEntity = new HttpEntity<String>(json, headers);
ResponseEntity<String> responseEntity =
getRestTemplate().exchange(getUrl("/v2/resource_match"), HttpMethod.PUT, requestEntity, String.class);
List<CloudResource> cloudResources = JsonUtil.convertJsonToCloudResourceList(responseEntity.getBody());
return new CloudResources(cloudResources);
}
private HttpEntity<MultiValueMap<String, ?>> generatePartialResourceRequest(UploadApplicationPayload application,
CloudResources knownRemoteResources) throws IOException {
MultiValueMap<String, Object> body = new LinkedMultiValueMap<String, Object>(2);
body.add("application", application);
ObjectMapper mapper = new ObjectMapper();
String knownRemoteResourcesPayload = mapper.writeValueAsString(knownRemoteResources);
body.add("resources", knownRemoteResourcesPayload);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
return new HttpEntity<MultiValueMap<String, ?>>(body, headers);
}
public void startApplication(String appName) {
CloudApplication app = getApplication(appName);
if (app.getState() != CloudApplication.AppState.STARTED) {
HashMap<String, Object> appRequest = new HashMap<String, Object>();
appRequest.put("state", CloudApplication.AppState.STARTED);
getRestTemplate().put(getUrl("/v2/apps/{guid}"), appRequest, app.getMeta().getGuid());
}
}
public void debugApplication(String appName, CloudApplication.DebugMode mode) {
throw new UnsupportedOperationException("Feature is not yet implemented.");
}
public void stopApplication(String appName) {
CloudApplication app = getApplication(appName);
if (app.getState() != CloudApplication.AppState.STOPPED) {
HashMap<String, Object> appRequest = new HashMap<String, Object>();
appRequest.put("state", CloudApplication.AppState.STOPPED);
getRestTemplate().put(getUrl("/v2/apps/{guid}"), appRequest, app.getMeta().getGuid());
}
}
public void restartApplication(String appName) {
stopApplication(appName);
startApplication(appName);
}
public void deleteApplication(String appName) {
UUID appId = getAppId(appName);
doDeleteApplication(appId);
}
public void deleteAllApplications() {
List<CloudApplication> cloudApps = getApplications();
for (CloudApplication cloudApp : cloudApps) {
doDeleteApplication(cloudApp.getMeta().getGuid());
}
}
public void updateApplicationMemory(String appName, int memory) {
UUID appId = getAppId(appName);
HashMap<String, Object> appRequest = new HashMap<String, Object>();
appRequest.put("memory", memory);
getRestTemplate().put(getUrl("/v2/apps/{guid}"), appRequest, appId);
}
public void updateApplicationInstances(String appName, int instances) {
UUID appId = getAppId(appName);
HashMap<String, Object> appRequest = new HashMap<String, Object>();
appRequest.put("instances", instances);
getRestTemplate().put(getUrl("/v2/apps/{guid}"), appRequest, appId);
}
public void updateApplicationServices(String appName, List<String> services) {
CloudApplication app = getApplication(appName);
List<UUID> addServices = new ArrayList<UUID>();
List<UUID> deleteServices = new ArrayList<UUID>();
// services to add
for (String serviceName : services) {
if (!app.getServices().contains(serviceName)) {
CloudService cloudService = getService(serviceName);
if (cloudService != null) {
addServices.add(cloudService.getMeta().getGuid());
}
else {
throw new CloudFoundryException(HttpStatus.NOT_FOUND, "Service with name " + serviceName +
" not found in current space " + sessionSpace.getName());
}
}
}
// services to delete
for (String serviceName : app.getServices()) {
if (!services.contains(serviceName)) {
CloudService cloudService = getService(serviceName);
if (cloudService != null) {
deleteServices.add(cloudService.getMeta().getGuid());
}
}
}
for (UUID serviceId : addServices) {
doBindService(app.getMeta().getGuid(), serviceId);
}
for (UUID serviceId : deleteServices) {
doUnbindService(app.getMeta().getGuid(), serviceId);
}
}
private void doBindService(UUID appId, UUID serviceId) {
HashMap<String, Object> serviceRequest = new HashMap<String, Object>();
serviceRequest.put("service_instance_guid", serviceId);
serviceRequest.put("app_guid", appId);
getRestTemplate().postForObject(getUrl("/v2/service_bindings"), serviceRequest, String.class);
}
private void doUnbindService(UUID appId, UUID serviceId) {
UUID serviceBindingId = getServiceBindingId(appId, serviceId);
getRestTemplate().delete(getUrl("/v2/service_bindings/{guid}"), serviceBindingId);
}
public void updateApplicationStaging(String appName, Staging staging) {
UUID appId = getAppId(appName);
HashMap<String, Object> appRequest = new HashMap<String, Object>();
if (staging.getCommand() != null) {
appRequest.put("command", staging.getCommand());
}
appRequest.put("framework_guid", getFrameworkId(staging.getFramework()));
appRequest.put("runtime_guid", getRuntimeId(staging.getRuntime()));
getRestTemplate().put(getUrl("/v2/apps/{guid}"), appRequest, appId);
}
public void updateApplicationUris(String appName, List<String> uris) {
CloudApplication app = getApplication(appName);
List<String> newUris = new ArrayList<String>(uris);
newUris.removeAll(app.getUris());
List<String> removeUris = app.getUris();
removeUris.removeAll(uris);
addUris(newUris, app.getMeta().getGuid());
removeUris(removeUris, app.getMeta().getGuid());
}
public void updateApplicationEnv(String appName, Map<String, String> env) {
UUID appId = getAppId(appName);
HashMap<String, Object> appRequest = new HashMap<String, Object>();
appRequest.put("environment_json", env);
getRestTemplate().put(getUrl("/v2/apps/{guid}"), appRequest, appId);
}
public void updateApplicationEnv(String appName, List<String> env) {
Map<String, String> envHash = new HashMap<String, String>();
for (String s : env) {
if (!s.contains("=")) {
throw new IllegalArgumentException("Environment setting without '=' is invalid: " + s);
}
String key = s.substring(0, s.indexOf('=')).trim();
String value = s.substring(s.indexOf('=') + 1).trim();
envHash.put(key, value);
}
updateApplicationEnv(appName, envHash);
}
public String getFile(String appName, int instanceIndex, String filePath, int startPosition, int endPosition) {
UUID appId = getAppId(appName);
String urlPath = "/v2/apps/{appId}/instances/{instanceIndex}/files/{filePath}";
return doGetFile(urlPath, appId, instanceIndex, filePath, startPosition, endPosition);
}
public void bindService(String appName, String serviceName) {
CloudService cloudService = getService(serviceName);
UUID appId = getAppId(appName);
doBindService(appId, cloudService.getMeta().getGuid());
}
public void unbindService(String appName, String serviceName) {
CloudService cloudService = getService(serviceName);
UUID appId = getAppId(appName);
doUnbindService(appId, cloudService.getMeta().getGuid());
}
public InstancesInfo getApplicationInstances(String appName) {
UUID appId = getAppId(appName);
CloudApplication app = getApplication(appName);
List<Map<String, Object>> instanceList = new ArrayList<Map<String, Object>>();
if (app.getState().equals(CloudApplication.AppState.STARTED)) {
Map<String, Object> respMap = getInstanceInfoForApp(appId, "instances");
List<String> keys = new ArrayList<String>(respMap.keySet());
java.util.Collections.sort(keys);
for (String instanceId : keys) {
Integer index;
try {
index = Integer.valueOf(instanceId);
} catch (NumberFormatException e) {
index = -1;
}
Map<String, Object> instanceMap = (Map<String, Object>) respMap.get(instanceId);
instanceMap.put("index", index);
instanceList.add(instanceMap);
}
}
return new InstancesInfo(instanceList);
}
public CrashesInfo getCrashes(String appName) {
throw new UnsupportedOperationException("Feature is not yet implemented.");
}
public void rename(String appName, String newName) {
UUID appId = getAppId(appName);
HashMap<String, Object> appRequest = new HashMap<String, Object>();
appRequest.put("name", newName);
getRestTemplate().put(getUrl("/v2/apps/{guid}"), appRequest, appId);
}
private void doDeleteService(CloudService cloudService) {
List<UUID> appIds = getAppsBoundToService(cloudService);
if (appIds.size() > 0) {
for (UUID appId : appIds) {
doUnbindService(appId, cloudService.getMeta().getGuid());
}
}
getRestTemplate().delete(getUrl("/v2/service_instances/{guid}"), cloudService.getMeta().getGuid());
}
private List<UUID> getAppsBoundToService(CloudService cloudService) {
List<UUID> appGuids = new ArrayList<UUID>();
String urlPath = "/v2";
Map<String, Object> urlVars = new HashMap<String, Object>();
if (sessionSpace != null) {
urlVars.put("space", sessionSpace.getMeta().getGuid());
urlPath = urlPath + "/spaces/{space}";
}
urlVars.put("q", "name:" + cloudService.getName());
urlPath = urlPath + "/service_instances?inline-relations-depth=2&q={q}";
List<Map<String, Object>> resourceList = getAllResources(urlPath, urlVars);
for (Map<String, Object> resource : resourceList) {
List<Map<String, Object>> bindings =
CloudEntityResourceMapper.getEntityAttribute(resource, "service_bindings", List.class);
for (Map<String, Object> binding : bindings) {
String appId = CloudEntityResourceMapper.getEntityAttribute(binding, "app_guid", String.class);
if (appId != null) {
appGuids.add(UUID.fromString(appId));
}
}
}
return appGuids;
}
private void doDeleteApplication(UUID appId) {
getRestTemplate().delete(getUrl("/v2/apps/{guid}"), appId);
}
@SuppressWarnings("unchecked")
private Collection<CloudInfo.Framework> getInfoForFrameworks(Map<String, CloudInfo.Runtime> runtimes) {
List<Map<String, Object>> resourceList = getAllResources("/v2/frameworks", null);
Collection<CloudInfo.Framework> frameworks = new ArrayList<CloudInfo.Framework>();
for (Map<String, Object> frameworkMap : resourceList) {
Map<String, Object> frameworkEntity = (Map<String, Object>) frameworkMap.get("entity");
String frameworkName = String.valueOf(frameworkEntity.get("name"));
String[] runtimePatterns = FRAMEWORK_RUNTIME_PATTERNS.get(frameworkName);
List<CloudInfo.Runtime> runtimeList = new ArrayList<CloudInfo.Runtime>();
if (runtimePatterns != null) {
for (String runtimePattern : runtimePatterns) {
Pattern pattern = Pattern.compile(runtimePattern);
for (String runtimeName : runtimes.keySet()) {
Matcher matcher = pattern.matcher(runtimeName);
if (matcher.find()) {
runtimeList.add(runtimes.get(runtimeName));
}
}
}
}
CloudInfo.Framework framework = new CloudInfo.Framework(frameworkEntity, runtimeList);
frameworks.add(framework);
}
return frameworks;
}
@SuppressWarnings("unchecked")
private Map<String, CloudInfo.Runtime> getInfoForRuntimes() {
List<Map<String, Object>> runtimesList = getAllResources("/v2/runtimes", null);
Map<String, CloudInfo.Runtime> runtimes = new HashMap<String, CloudInfo.Runtime>();
for (Map<String, Object> runtimeMap : runtimesList) {
Map<String, Object> runtimeEntity = (Map<String, Object>) runtimeMap.get("entity");
CloudInfo.Runtime runtime = new CloudInfo.Runtime(runtimeEntity);
runtimes.put(runtime.getName(), runtime);
}
return runtimes;
}
@SuppressWarnings("unchecked")
private List<CloudServiceOffering> getServiceOfferings(String label) {
Assert.notNull(label, "Service label must not be null");
List<Map<String, Object>> resourceList = getAllResources("/v2/services?inline-relations-depth=2", null);
List<CloudServiceOffering> results = new ArrayList<CloudServiceOffering>();
for (Map<String, Object> resource : resourceList) {
CloudServiceOffering cloudServiceOffering =
resourceMapper.mapResource(resource, CloudServiceOffering.class);
if (cloudServiceOffering.getLabel() != null && label.equals(cloudServiceOffering.getLabel())) {
results.add(cloudServiceOffering);
}
}
return results;
}
@SuppressWarnings("unchecked")
private UUID getServiceBindingId(UUID appId, UUID serviceId ) {
Map<String, Object> urlVars = new HashMap<String, Object>();
urlVars.put("guid", appId);
List<Map<String, Object>> resourceList = getAllResources("/v2/apps/{guid}/service_bindings", urlVars);
UUID serviceBindingId = null;
if (resourceList != null && resourceList.size() > 0) {
for (Map<String, Object> resource : resourceList) {
Map<String, Object> bindingMeta = (Map<String, Object>) resource.get("metadata");
Map<String, Object> bindingEntity = (Map<String, Object>) resource.get("entity");
String serviceInstanceGuid = (String) bindingEntity.get("service_instance_guid");
if (serviceInstanceGuid != null && serviceInstanceGuid.equals(serviceId.toString())) {
String bindingGuid = (String) bindingMeta.get("guid");
serviceBindingId = UUID.fromString(bindingGuid);
break;
}
}
}
return serviceBindingId;
}
@SuppressWarnings("unchecked")
private UUID getAppId(String appName) {
Map<String, Object> resource = findApplicationResource(appName, 1);
UUID guid = null;
if (resource != null) {
Map<String, Object> appMeta = (Map<String, Object>) resource.get("metadata");
guid = UUID.fromString(String.valueOf(appMeta.get("guid")));
}
return guid;
}
private Map<String, Object> findApplicationResource(String appName, int depth) {
Map<String, Object> urlVars = new HashMap<String, Object>();
String urlPath = "/v2";
if (sessionSpace != null) {
urlVars.put("space", sessionSpace.getMeta().getGuid());
urlPath = urlPath + "/spaces/{space}";
}
urlVars.put("q", "name:" + appName);
urlPath = urlPath + "/apps?inline-relations-depth={depth}&q={q}";
urlVars.put("depth", depth);
List<Map<String, Object>> resourceList = getAllResources(urlPath, urlVars);
if (resourceList.size() > 0) {
return resourceList.get(0);
}
else {
return null;
}
}
private List<String> findApplicationUris(UUID appGuid) {
Map<String, Object> urlVars = new HashMap<String, Object>();
String urlPath = "/v2/apps/{app}/routes?inline-relations-depth={depth}";
urlVars.put("app", appGuid);
urlVars.put("depth", 2);
List<Map<String, Object>> resourceList = getAllResources(urlPath, urlVars);
List<String> uris = new ArrayList<String>();
for (Map<String, Object> resource : resourceList) {
Map<String, Object> domainResource = CloudEntityResourceMapper.getEmbeddedResource(resource, "domain");
String uri = CloudEntityResourceMapper.getEntityAttribute(resource, "host", String.class) + "." +
CloudEntityResourceMapper.getEntityAttribute(domainResource, "name", String.class);
uris.add(uri);
}
return uris;
}
@SuppressWarnings("unchecked")
private UUID getFrameworkId(String framework) {
if (!frameworkIdCache.containsKey(framework)) {
List<Map<String, Object>> resourceList = getAllResources("/v2/frameworks", null);
for (Map<String, Object> resource : resourceList) {
String name = resourceMapper.getNameOfResource(resource);
UUID guid = resourceMapper.getGuidOfResource(resource);
frameworkIdCache.put(name, guid);
}
}
return frameworkIdCache.get(framework);
}
@SuppressWarnings("unchecked")
private UUID getRuntimeId(String runtime) {
if (!runtimeIdCache.containsKey(runtime)) {
List<Map<String, Object>> resourceList = getAllResources("/v2/runtimes", null);
for (Map<String, Object> resource : resourceList) {
String name = resourceMapper.getNameOfResource(resource);
UUID guid = resourceMapper.getGuidOfResource(resource);
runtimeIdCache.put(name, guid);
}
}
return runtimeIdCache.get(runtime);
}
@SuppressWarnings("unused")
private Map<String, Object> getUserInfo(String user) {
// String userJson = getRestTemplate().getForObject(getUrl("/v2/users/{guid}"), String.class, user);
// Map<String, Object> userInfo = (Map<String, Object>) JsonUtil.convertJsonToMap(userJson);
// return userInfo();
//TODO: remove this temporary hack once the /v2/users/ uri can be accessed by mere mortals
String userJson = "{}";
if (token != null) {
int x = token.indexOf('.');
int y = token.indexOf('.', x + 1);
String encodedString = token.substring(x + 1, y);
try {
byte[] decodedBytes = new sun.misc.BASE64Decoder().decodeBuffer(encodedString);
userJson = new String(decodedBytes, 0, decodedBytes.length, "UTF-8");
} catch (IOException e) {}
}
return(JsonUtil.convertJsonToMap(userJson));
}
}