/******************************************************************************* * Copyright (c) 2015, 2017 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are 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: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.boot.dash.cloudfoundry; import static org.springframework.ide.eclipse.boot.dash.model.RunState.DEBUGGING; import static org.springframework.ide.eclipse.boot.dash.model.RunState.INACTIVE; import static org.springframework.ide.eclipse.boot.dash.model.RunState.RUNNING; import static org.springframework.ide.eclipse.boot.dash.model.RunState.STARTING; import static org.springframework.ide.eclipse.boot.dash.views.sections.BootDashColumn.DEFAULT_PATH; import static org.springframework.ide.eclipse.boot.dash.views.sections.BootDashColumn.HOST; import static org.springframework.ide.eclipse.boot.dash.views.sections.BootDashColumn.INSTANCES; import static org.springframework.ide.eclipse.boot.dash.views.sections.BootDashColumn.NAME; import static org.springframework.ide.eclipse.boot.dash.views.sections.BootDashColumn.PROJECT; import static org.springframework.ide.eclipse.boot.dash.views.sections.BootDashColumn.RUN_STATE_ICN; import static org.springframework.ide.eclipse.boot.dash.views.sections.BootDashColumn.TAGS; import java.util.Arrays; import java.util.Collection; import java.util.EnumSet; import java.util.List; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.osgi.framework.Version; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFBuildpack; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFCloudDomain; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFSpace; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFStack; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.ClientRequests; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CloudFoundryClientFactory; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.SshClientSupport; import org.springframework.ide.eclipse.boot.dash.metadata.PropertyStoreApi; import org.springframework.ide.eclipse.boot.dash.model.AbstractBootDashModel; import org.springframework.ide.eclipse.boot.dash.model.AbstractRunTarget; import org.springframework.ide.eclipse.boot.dash.model.BootDashModelContext; import org.springframework.ide.eclipse.boot.dash.model.BootDashViewModel; import org.springframework.ide.eclipse.boot.dash.model.RunState; import org.springframework.ide.eclipse.boot.dash.model.RunTargetWithProperties; import org.springframework.ide.eclipse.boot.dash.model.runtargettypes.RunTargetType; import org.springframework.ide.eclipse.boot.dash.views.sections.BootDashColumn; import org.springframework.ide.eclipse.boot.util.Log; import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression; import org.springsource.ide.eclipse.commons.livexp.core.LiveVariable; import org.springsource.ide.eclipse.commons.livexp.core.ValueListener; public class CloudFoundryRunTarget extends AbstractRunTarget implements RunTargetWithProperties { private CloudFoundryTargetProperties targetProperties; // Cache these to avoid frequent client calls private List<CFCloudDomain> domains; private List<CFSpace> spaces; private List<CFStack> stacks; private LiveVariable<ClientRequests> cachedClient; private CloudFoundryClientFactory clientFactory; public CloudFoundryRunTarget(CloudFoundryTargetProperties targetProperties, RunTargetType runTargetType, CloudFoundryClientFactory clientFactory) { super(runTargetType, CloudFoundryTargetProperties.getId(targetProperties), CloudFoundryTargetProperties.getName(targetProperties)); this.targetProperties = targetProperties; this.clientFactory = clientFactory; this.cachedClient = new LiveVariable<>(); } public static final EnumSet<RunState> RUN_GOAL_STATES = EnumSet.of(INACTIVE, STARTING, RUNNING, DEBUGGING); private static final BootDashColumn[] DEFAULT_COLUMNS = { RUN_STATE_ICN, NAME, PROJECT, INSTANCES, DEFAULT_PATH, TAGS }; private static final BootDashColumn[] ALL_COLUMNS = { RUN_STATE_ICN, NAME, PROJECT, INSTANCES, HOST, DEFAULT_PATH, TAGS }; private static final String APPS_MANAGER_HOST = "APPS_MANAGER_HOST"; private static final String BUILDPACKS = "BUILDPACKS"; @Override public ILaunchConfiguration createLaunchConfig(IJavaProject jp, IType mainType) throws Exception { return null; } public ClientRequests getClient() { return cachedClient.getValue(); } public void connect() throws Exception { try { this.domains = null; this.spaces = null; this.stacks = null; cachedClient.setValue(createClient()); if (getClient() != null) { persistBuildpacks(getClient().getBuildpacks()); } } catch (Exception e) { cachedClient.setValue(null); throw e; } } public void disconnect() { this.domains = null; this.spaces = null; this.stacks = null; if (getClient() != null) { getClient().logout(); cachedClient.setValue(null); } } protected void persistBuildpacks(List<CFBuildpack> buildpacks) throws Exception { PropertyStoreApi properties = getPersistentProperties(); if (properties != null) { String[] buildpackVals = null; if (buildpacks != null && !buildpacks.isEmpty()) { buildpackVals = new String[buildpacks.size()]; for (int i = 0; i < buildpacks.size(); i++) { buildpackVals[i] = buildpacks.get(i).getName(); } } properties.put(BUILDPACKS, buildpackVals); } } public boolean isConnected() { return cachedClient.getValue() != null; } public void addConnectionStateListener(ValueListener<ClientRequests> l) { cachedClient.addListener(l); } public void removeConnectionStateListener(ValueListener<ClientRequests> l) { cachedClient.removeListener(l); } public Version getCCApiVersion() { try { ClientRequests client = getClient(); if (client!=null) { return client.getApiVersion(); } } catch (Exception e) { Log.log(e); } return null; } public CloudFoundryClientFactory getClientFactory() { return clientFactory; } protected ClientRequests createClient() throws Exception { return clientFactory.getClient(this); } @Override public BootDashColumn[] getDefaultColumns() { return DEFAULT_COLUMNS; } @Override public BootDashColumn[] getAllColumns() { return ALL_COLUMNS; } @Override public AbstractBootDashModel createElementsTabelModel(BootDashModelContext context, BootDashViewModel parent) { return new CloudFoundryBootDashModel(this, context, parent); } @Override public CloudFoundryTargetProperties getTargetProperties() { return targetProperties; } @Override public boolean canRemove() { return true; } @Override public boolean canDeployAppsTo() { return true; } @Override public boolean canDeployAppsFrom() { return false; } @Override public void refresh() throws Exception { // Fetching a new client always validates the CF credentials disconnect(); connect(); } @Override public boolean requiresCredentials() { return true; } public synchronized List<CFCloudDomain> getDomains( IProgressMonitor monitor) throws Exception { if (domains == null) { SubMonitor subMonitor = SubMonitor.convert(monitor, 10); subMonitor.beginTask("Refreshing list of domains for " + getName(), 5); domains = getClient().getDomains(); subMonitor.worked(5); } return domains; } public synchronized List<CFStack> getStacks(IProgressMonitor monitor) throws Exception { if (stacks == null) { SubMonitor subMonitor = SubMonitor.convert(monitor, 10); subMonitor.beginTask("Refreshing list of stacks for " + getName(), 5); stacks = getClient().getStacks(); subMonitor.worked(5); } return stacks; } public synchronized List<CFSpace> getSpaces(ClientRequests requests, IProgressMonitor monitor) throws Exception { if (spaces == null) { SubMonitor subMonitor = SubMonitor.convert(monitor, 10); subMonitor.beginTask("Refreshing list of spaces for " + getName(), 5); spaces = requests.getSpaces(); subMonitor.worked(5); } return spaces; } public boolean isPWS() { return "https://api.run.pivotal.io".equals(getUrl()); } public String getUrl() { String url = targetProperties.getUrl(); while (url.endsWith("/")) { url = url.substring(0, url.length()-1); } return url; } public SshClientSupport getSshClientSupport() throws Exception { ClientRequests client = getClient(); return client.getSshClientSupport(); } /** * Returns list of cached buildpacks. It does NOT fetch updated buildpacks from CF as this method may * be called in cases where fast list of buildpacks is required (e.g values that show in pop-up UI). * <p/> * Do NOT use this method to fetch buildpacks from CF * @return Collection of CACHED buildpacks, or null if none cached yet. * @throws Exception */ public Collection<String> getBuildpackValues() throws Exception { PropertyStoreApi properties = getPersistentProperties(); if (properties != null) { String[] buildPackVals = properties.get(BUILDPACKS, (String[]) null); if (buildPackVals != null) { return Arrays.asList(buildPackVals); } } return null; } public String getBuildpack(IProject project) { // Only support Java project for now IJavaProject javaProject = JavaCore.create(project); if (javaProject != null) { try { Collection<String> buildpacks = getBuildpackValues(); if (buildpacks != null) { String javaBuildpack = null; // Only chose a java build iff ONE java buildpack exists // that contains the java_buildpack pattern. for (String bp : buildpacks) { // iterate through all buildpacks to make sure only // ONE java buildpack exists if (bp.contains("java_buildpack")) { if (javaBuildpack == null) { javaBuildpack = bp; } else { // If more than two buildpacks contain // "java_buildpack", do not chose any. Let CF buildpack // detection decided which one to chose. javaBuildpack = null; break; } } } return javaBuildpack; } } catch (Exception e) { Log.log(e); } } return null; } @Override public String getTemplateVar(char name) { switch (name) { case 'o': return getTargetProperties().getOrganizationName(); case 's': return getTargetProperties().getSpaceName(); case 'a': return getTargetProperties().getUrl(); case 'u': return getTargetProperties().getUsername(); default: return super.getTemplateVar(name); } } public String getAppsManagerHost() { PropertyStoreApi props = getPersistentProperties(); if (props != null) { String appsManagerURL = props.get(APPS_MANAGER_HOST); if (appsManagerURL != null) { return appsManagerURL; } } return getAppsManagerHostDefault(); } public void setAppsManagerHost(String appsManagerURL) throws Exception { getPersistentProperties().put(APPS_MANAGER_HOST, appsManagerURL); } public String getAppsManagerURL() { String host = getAppsManagerHost(); CloudFoundryTargetProperties targetProperties = getTargetProperties(); String org = targetProperties.getOrganizationGuid(); String space = targetProperties.getSpaceGuid(); if (host != null && host.length() > 0 && org != null && org.length() > 0 && space != null && space.length() > 0) { return host + "/organizations/" + org + "/spaces/" + space; } else { return null; } } public String getAppsManagerHostDefault() { String url = getUrl(); if (url != null && url.contains("//api.")) { return url.replace("//api.", "//console."); } else { return null; } } public LiveExpression<ClientRequests> getClientExp() { return cachedClient; } }