Merge pull request #6 from nierajsingh/cf_support

Cf support
This commit is contained in:
nierajsingh
2017-01-07 12:44:02 -08:00
committed by GitHub
54 changed files with 4267 additions and 10 deletions

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>commons-cf</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.springframework.ide.eclipse.core.springbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.springframework.ide.eclipse.core.springnature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,5 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.8

View File

@@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

View File

@@ -0,0 +1,42 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>commons-cf</artifactId>
<name>commons-cf</name>
<description>Common code related to 'accessing Cloud Foundry'</description>
<parent>
<groupId>org.springframework.ide.vscode</groupId>
<artifactId>commons-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.ide.vscode</groupId>
<artifactId>commons-util</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.cloudfoundry</groupId>
<artifactId>cloudfoundry-client-reactor</artifactId>
<version>${cloudfoundry-client-version}</version>
</dependency>
<dependency>
<groupId>org.cloudfoundry</groupId>
<artifactId>cloudfoundry-operations</artifactId>
<version>${cloudfoundry-client-version}</version>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>${reactor-version}</version>
</dependency>
<dependency>
<groupId>io.projectreactor.ipc</groupId>
<artifactId>reactor-netty</artifactId>
<version>${reactor-netty}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,19 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client;
public enum CFAppState {
STOPPED,
STARTED,
UNKNOWN
}

View File

@@ -0,0 +1,35 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public interface CFApplication extends CFEntity {
//TODO: lots of this infos should be moved to application details
int getInstances();
int getRunningInstances();
int getMemory();
UUID getGuid();
List<String> getServices();
String getBuildpackUrl();
List<String> getUris();
CFAppState getState();
int getDiskQuota();
Integer getTimeout();
String getCommand();
String getStack();
Map<String,String> getEnvAsMap();
String getHealthCheckType();
}

View File

@@ -0,0 +1,15 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client;
public interface CFApplicationArchive {
}

View File

@@ -0,0 +1,17 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client;
import java.util.List;
public interface CFApplicationDetail extends CFApplication {
List<CFInstanceStats> getInstanceDetails();
}

View File

@@ -0,0 +1,15 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client;
public interface CFBuildpack {
String getName();
}

View File

@@ -0,0 +1,15 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client;
public interface CFCloudDomain {
String getName();
}

View File

@@ -0,0 +1,15 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client;
public interface CFEntity {
String getName();
}

View File

@@ -0,0 +1,27 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client;
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
/**
* Static methods to recognize specific types of exceptions CF client
* may throw.
*
* @author Kris De Volder
*/
public class CFExceptions {
public static boolean isSSLCertificateFailure(Exception e) {
Throwable cause = ExceptionUtil.getDeepestCause(e);
return cause.getClass().getName().equals("sun.security.provider.certpath.SunCertPathBuilderException");
}
}

View File

@@ -0,0 +1,15 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client;
public enum CFInstanceState {
RUNNING, CRASHED, FLAPPING, STARTING, DOWN, UNKNOWN
}

View File

@@ -0,0 +1,17 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client;
public interface CFInstanceStats {
CFInstanceState getState();
}

View File

@@ -0,0 +1,17 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client;
import java.util.UUID;
public interface CFOrganization extends CFEntity {
UUID getGuid();
}

View File

@@ -0,0 +1,24 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client;
public interface CFServiceInstance extends CFEntity {
String getName();
String getService();
String getPlan();
String getDescription();
String getDocumentationUrl();
String getDashboardUrl();
//TODO: last operation info?
}

View File

@@ -0,0 +1,18 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client;
import java.util.UUID;
public interface CFSpace extends CFEntity {
CFOrganization getOrganization();
UUID getGuid();
}

View File

@@ -0,0 +1,15 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client;
public interface CFStack extends CFEntity {
}

View File

@@ -0,0 +1,19 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client;
public class HealthChecks {
public static final String HC_NONE = "none";
public static final String HC_PORT = "port";
public static final String[] HC_ALL = {HC_NONE, HC_PORT};
}

View File

@@ -0,0 +1,30 @@
/*******************************************************************************
* Copyright (c) 2009-2016 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.vscode.commons.cloudfoundry.client;
/**
* Enum used for the state of an instance
*
* Note: copied over from CF V1 client code.
*
* @author Thomas Risberg
*/
public enum InstanceState {
DOWN, STARTING, RUNNING, CRASHED, FLAPPING, UNKNOWN;
public static InstanceState valueOfWithDefault(String s) {
try {
return InstanceState.valueOf(s);
} catch (IllegalArgumentException e) {
return InstanceState.UNKNOWN;
}
}
}

View File

@@ -0,0 +1,27 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client;
public enum LoginMethod {
PASSWORD,
TEMPORARY_CODE;
public String getLabel() {
String[] pieces = name().split("_");
StringBuilder label = new StringBuilder();
for (int i = 0; i < pieces.length; i++) {
if (i>0) {
label.append(" ");
}
label.append(pieces[i].toLowerCase());
}
return label.toString();
}
}

View File

@@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client;
public class RequestErrorHandler {
/**
*
* @param e
* @return true if request error should be treated as an error and thrown. False error
* should be ignored.
*/
public boolean throwError(Throwable e) {
return true;
}
}

View File

@@ -0,0 +1,27 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal Software, 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 Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.cloudfoundry.client;
import java.util.UUID;
public interface SshClientSupport {
SshHost getSshHost() throws Exception;
String getSshUser(String appName, int instance) throws Exception;
String getSshCode() throws Exception;
/**
* Deprecated because it is not supported with V2. Use the method based on appName instead.
*/
@Deprecated
String getSshUser(UUID appGuid, int instance) throws Exception;
}

View File

@@ -0,0 +1,53 @@
/*******************************************************************************
* Copyright (c) 2015, 2016 Pivotal Software, 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 Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.cloudfoundry.client;
/**
* Info object containing various bits of info about the host to which an ssh
* client may wish to connect.
*
* @author Kris De Volder
*/
public class SshHost {
final private String host;
final private int port;
final private String fingerPrint;
public SshHost(String host, int port, String fingerPrint) {
super();
this.host = host;
this.port = port;
this.fingerPrint = fingerPrint;
}
public String getFingerPrint() {
return fingerPrint;
}
public int getPort() {
return port;
}
public String getHost() {
return host;
}
@Override
public String toString() {
return "SshHost [host=" //$NON-NLS-1$
+ host + ", port=" //$NON-NLS-1$
+ port + ", fingerPrint=" //$NON-NLS-1$
+ fingerPrint + "]";//$NON-NLS-1$
}
}

View File

@@ -0,0 +1,159 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client.target;
import java.net.URI;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.ide.vscode.commons.cloudfoundry.client.target.CFCredentials.CFCredentialType;
import org.springframework.ide.vscode.commons.util.Assert;
/**
* All the parameters needed to create a CF client.
*
* @author Kris De Volder
*/
public class CFClientParams {
private static final Logger logger = Logger.getLogger(CFClientParams.class.getName());
private final String apiUrl;
private final String username;
private CFCredentials credentials;
private final boolean skipSslValidation;
private String orgName; // optional
private String spaceName; //optional
public CFClientParams(String apiUrl,
String username,
CFCredentials credentials,
String orgName,
String spaceName,
boolean skipSslValidation
) {
Assert.isNotNull(apiUrl);
Assert.isNotNull(credentials);
if (credentials.getType()==CFCredentialType.PASSWORD) {
Assert.isNotNull(username);
}
this.apiUrl = apiUrl;
this.username = username;
this.credentials = credentials;
this.skipSslValidation = skipSslValidation;
this.orgName = orgName;
this.spaceName = spaceName;
}
public CFCredentials getCredentials() {
return credentials;
}
public String getUsername() {
return username;
}
public boolean skipSslValidation() {
return skipSslValidation;
}
public String getApiUrl() {
return apiUrl;
}
public String getOrgName() {
return orgName;
}
public void setOrgName(String orgName) {
this.orgName = orgName;
}
public String getSpaceName() {
return spaceName;
}
public void setSpaceName(String spaceName) {
this.spaceName = spaceName;
}
public String getHost() {
try {
URI uri = new URI(getApiUrl());
return uri.getHost();
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
return null;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((apiUrl == null) ? 0 : apiUrl.hashCode());
result = prime * result + ((orgName == null) ? 0 : orgName.hashCode());
result = prime * result + ((credentials == null) ? 0 : credentials.hashCode());
result = prime * result + ((spaceName == null) ? 0 : spaceName.hashCode());
result = prime * result + ((username == null) ? 0 : username.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CFClientParams other = (CFClientParams) obj;
if (apiUrl == null) {
if (other.apiUrl != null)
return false;
} else if (!apiUrl.equals(other.apiUrl))
return false;
if (orgName == null) {
if (other.orgName != null)
return false;
} else if (!orgName.equals(other.orgName))
return false;
if (credentials == null) {
if (other.credentials != null)
return false;
} else if (!credentials.equals(other.credentials))
return false;
if (spaceName == null) {
if (other.spaceName != null)
return false;
} else if (!spaceName.equals(other.spaceName))
return false;
if (username == null) {
if (other.username != null)
return false;
} else if (!username.equals(other.username))
return false;
return true;
}
@Override
public String toString() {
return "CFClientParams [apiUrl=" + apiUrl + ", username=" + username + ", credentials=" + credentials
+ ", skipSslValidation=" + skipSslValidation + ", orgName=" + orgName + ", spaceName=" + spaceName
+ "]";
}
}

View File

@@ -0,0 +1,59 @@
/*******************************************************************************
* Copyright (c) 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.vscode.commons.cloudfoundry.client.target;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Provider;
/**
* Resolves Cloud Foundry client parameters from registered params providers
*
*/
public class CFClientParamsFactory {
public static final CFClientParamsFactory INSTANCE = new CFClientParamsFactory();
@SuppressWarnings("unchecked")
private List<Provider<List<CFClientParams>>> providers = new ArrayList<>();
private CFClientParamsFactory() {
// Singleton
addProvider(new CfCliParamsProvider());
}
/**
* Adds provider to the front of the registered providers such that it would
* be called first when a request is made for CF client params
*
* @param provider
*/
public void addProvider(Provider<List<CFClientParams>> provider) {
if (provider != null) {
providers.add(provider);
}
}
public List<CFClientParams> getParams() {
// Start from the last provider, which is the highest priority
for (int i = providers.size() - 1; i >= 0; i--) {
List<CFClientParams> params = providers.get(i).get();
if (params != null && !params.isEmpty()) {
return params;
}
}
return null;
}
}

View File

@@ -0,0 +1,132 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client.target;
import org.springframework.ide.vscode.commons.cloudfoundry.client.LoginMethod;
import org.springframework.ide.vscode.commons.util.Assert;
public class CFCredentials {
public enum CFCredentialType {
PASSWORD,
TEMPORARY_CODE,
REFRESH_TOKEN;
public LoginMethod toLoginMethod() {
switch (this) {
case PASSWORD:
return LoginMethod.PASSWORD;
case TEMPORARY_CODE:
return LoginMethod.TEMPORARY_CODE;
default:
return null;
}
}
}
private final CFCredentialType type;
private final String secret;
/**
* Deprecated, use fromLogin instead
*/
@Deprecated
public static CFCredentials fromPassword(String password) {
return fromLogin(LoginMethod.PASSWORD, password);
}
public static CFCredentials fromLogin(LoginMethod method, String secret) {
CFCredentialType type;
switch (method) {
case PASSWORD:
type = CFCredentialType.PASSWORD;
break;
case TEMPORARY_CODE:
type = CFCredentialType.TEMPORARY_CODE;
break;
default:
throw new IllegalStateException("Bug! Missing switch case?");
}
return new CFCredentials(type, secret);
}
public static CFCredentials fromRefreshToken(String refreshToken) {
Assert.isNotNull(refreshToken);
return new CFCredentials(CFCredentialType.REFRESH_TOKEN, refreshToken);
}
public String getSecret() {
return secret;
}
/////////////////////////////////////////////////////////////////////////
/**
* Private constuctor, use static `fromXXX` factory methods instead.
*/
private CFCredentials(CFCredentialType type, String secret) {
this.type = type;
this.secret = secret;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((secret == null) ? 0 : secret.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CFCredentials other = (CFCredentials) obj;
if (secret == null) {
if (other.secret != null)
return false;
} else if (!secret.equals(other.secret))
return false;
if (type != other.type)
return false;
return true;
}
@Override
public String toString() {
return "CFCredentials [type=" + type + ", secret=" + hidePassword(type, secret) + "]";
}
private String hidePassword(CFCredentialType type, String password) {
if (password==null) {
return null;
}
return type==CFCredentialType.PASSWORD
? "****"
: password;
}
public CFCredentialType getType() {
return type;
}
public static CFCredentials fromSsoToken(String ssoToken) {
return CFCredentials.fromLogin(LoginMethod.TEMPORARY_CODE, ssoToken);
}
}

View File

@@ -0,0 +1,113 @@
/*******************************************************************************
* Copyright (c) 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.vscode.commons.cloudfoundry.client.target;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Provider;
import org.springframework.ide.vscode.commons.util.ExternalCommand;
import org.springframework.ide.vscode.commons.util.ExternalProcess;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Provides existing Cloud Foundry client params, like target and credentials,
* from the CLI config.json in the file system.
*
*
*/
public class CfCliParamsProvider implements Provider<List<CFClientParams>> {
public static final String TARGET = "Target";
public static final String REFRESH_TOKEN = "RefreshToken";
public static final String ORGANIZATION_FIELDS = "OrganizationFields";
public static final String SPACE_FIELDS = "SpaceFields";
public static final String NAME = "Name";
public static final String SSL_DISABLED = "SSLDisabled";
private static Logger logger = Logger.getLogger(CfCliParamsProvider.class.getName());
@Override
public List<CFClientParams> get() {
try {
File file = getConfigJsonFile();
if (file != null) {
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> userData = mapper.readValue(file, Map.class);
if (userData != null) {
String refreshToken = (String) userData.get(REFRESH_TOKEN);
if (refreshToken == null) {
return null;
}
CFCredentials credentials = CFCredentials.fromRefreshToken(refreshToken);
boolean sslDisabled = (Boolean) userData.get(SSL_DISABLED);
String target = (String) userData.get(TARGET);
Map<String, Object> orgFields = (Map<String, Object>) userData.get(ORGANIZATION_FIELDS);
Map<String, Object> spaceFields = (Map<String, Object>) userData.get(SPACE_FIELDS);
if (target != null && orgFields != null && spaceFields != null) {
String orgName = (String) orgFields.get(NAME);
String spaceName = (String) spaceFields.get(NAME);
List<CFClientParams> params = new ArrayList<>();
params.add(new CFClientParams(target, null, credentials, orgName, spaceName, sslDisabled));
return params;
}
}
}
} catch (IOException | InterruptedException e) {
log(e);
}
return null;
}
private File getConfigJsonFile() throws IOException, InterruptedException {
if (!System.getProperty("os.name").toLowerCase().startsWith("win")) {
String homeDir = getUnixHomeDir();
if (homeDir != null) {
if (!homeDir.endsWith("/")) {
homeDir += '/';
}
String filePath = homeDir + ".cf/config.json";
File file = new File(filePath);
if (file.exists() && file.canRead()) {
return file;
}
}
}
return null;
}
private String getUnixHomeDir() throws IOException, InterruptedException {
File currentWorkDir = null; /*
* use current working dir which means pass
* null
*/
ExternalProcess homeDirProcess = new ExternalProcess(currentWorkDir,
new ExternalCommand("/bin/bash", "-c", "echo $HOME"), true);
String homeDir = homeDirProcess.getOut();
if (homeDir != null) {
homeDir = homeDir.trim(); // remove any new line chars
}
return homeDir;
}
private void log(Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
}

View File

@@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client.v2;
import java.util.List;
import java.util.Map;
import reactor.core.publisher.Mono;
/**
* Extrat bits of info that are 'prefetched' asynchronously to fill
* out the CFApplication with info that C2 client doesn't initially return.
*
* @author Kris De Volder
*/
public interface ApplicationExtras {
Mono<Map<String,String>> getEnv();
Mono<List<String>> getServices();
Mono<String> getBuildpack();
Mono<String> getStack();
Mono<Integer> getTimeout();
Mono<String> getCommand();
Mono<String> getHealthCheckType();
}

View File

@@ -0,0 +1,62 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client.v2;
import java.util.List;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFApplicationDetail;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFInstanceStats;
public class CFApplicationDetailData extends CFApplicationSummaryData implements CFApplicationDetail {
private List<CFInstanceStats> instanceDetails;
public CFApplicationDetailData(
CFApplicationSummaryData app,
List<CFInstanceStats> instanceDetails
) {
super(
app.getName(),
app.getInstances(),
app.getRunningInstances(),
app.getMemory(),
app.getGuid(),
app.getUris(),
app.getState(),
app.getDiskQuota(),
app.extras
);
this.instanceDetails = instanceDetails;
}
@Override
public List<CFInstanceStats> getInstanceDetails() {
return instanceDetails;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("CFApplicationDetail(\n");
buf.append(" name = "+getName()+"\n");
buf.append(" instance = "+getInstances()+"\n");
buf.append(" runningInstances = "+getRunningInstances()+"\n");
buf.append(" memory = "+getMemory()+"\n");
buf.append(" guid = "+getGuid()+"\n");
buf.append(" uris = "+getUris()+"\n");
buf.append(" state = "+getState()+"\n");
buf.append(" diskQuota = "+getDiskQuota()+"\n");
buf.append(" instanceDetails = "+getInstanceDetails()+"\n");
buf.append(")\n");
return buf.toString();
}
}

View File

@@ -0,0 +1,130 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client.v2;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFAppState;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFApplication;
public class CFApplicationSummaryData implements CFApplication {
private String name;
private int instances;
private int runningInstances;
private int memory;
private UUID guid;
private List<String> uris;
private CFAppState state;
private int diskQuota;
protected ApplicationExtras extras;
public CFApplicationSummaryData(
String name,
int instances,
int runningInstances,
int memory,
UUID guid,
List<String> uris,
CFAppState state,
int diskQuota,
ApplicationExtras extras
) {
super();
this.name = name;
this.instances = instances;
this.runningInstances = runningInstances;
this.memory = memory;
this.guid = guid;
this.uris = uris;
this.state = state;
this.diskQuota = diskQuota;
this.extras = extras;
}
@Override
public String getName() {
return name;
}
@Override
public int getInstances() {
return instances;
}
@Override
public int getRunningInstances() {
return runningInstances;
}
@Override
public int getMemory() {
return memory;
}
@Override
public UUID getGuid() {
return guid;
}
@Override
public List<String> getServices() {
return extras.getServices().block();
}
@Override
public String getBuildpackUrl() {
return extras.getBuildpack().block();
}
@Override
public List<String> getUris() {
return uris;
}
@Override
public CFAppState getState() {
return state;
}
@Override
public int getDiskQuota() {
return diskQuota;
}
@Override
public Integer getTimeout() {
return extras.getTimeout().block();
}
@Override
public String getHealthCheckType() {
return extras.getHealthCheckType().block();
}
@Override
public String getCommand() {
return extras.getCommand().block();
}
@Override
public String getStack() {
return extras.getStack().block();
}
@Override
public Map<String, String> getEnvAsMap() {
return extras.getEnv().block();
}
}

View File

@@ -0,0 +1,157 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client.v2;
import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipFile;
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
/**
* Arguments passed to push operation.
*
* @author Kris De Volder
* @author Nieraj Singh
*/
public class CFPushArguments implements AutoCloseable {
private List<String> routes = ImmutableList.of();
private String appName;
private Integer memory;
private Integer diskQuota;
private Integer timeout;
private String buildpack;
private String command;
private String stack;
private String healthCheckType;
private Map<String, String> env = ImmutableMap.of();
private Integer instances;
private List<String> services = ImmutableList.of();
private ZipFile applicationData;
private boolean noStart = false;
public CFPushArguments() {
}
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public Integer getMemory() {
return memory;
}
public void setMemory(Integer memory) {
this.memory = memory;
}
public Integer getDiskQuota() {
return diskQuota;
}
public void setDiskQuota(Integer diskQuota) {
this.diskQuota = diskQuota;
}
public Integer getTimeout() {
return timeout;
}
public void setTimeout(Integer timeout) {
this.timeout = timeout;
}
public String getBuildpack() {
return buildpack;
}
public void setBuildpack(String buildpack) {
this.buildpack = buildpack;
}
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
public String getStack() {
return stack;
}
public void setStack(String stack) {
this.stack = stack;
}
public Map<String, String> getEnv() {
return env;
}
public void setEnv(Map<String, String> env) {
this.env = env;
}
public Integer getInstances() {
return instances;
}
public void setInstances(Integer instances) {
this.instances = instances;
}
public List<String> getServices() {
return services;
}
public void setServices(List<String> services) {
this.services = services;
}
public ZipFile getApplicationData() {
return applicationData;
}
@Override
public void close() throws Exception {
if (applicationData!=null) {
applicationData.close();
}
}
public void setApplicationData(File archive) throws Exception {
if (applicationData==null) {
ExceptionUtil.exception("Can only set this once", null);
}
this.applicationData = new ZipFile(archive);
}
public boolean isNoStart() {
return noStart;
}
public void setNoStart(boolean noStart) {
this.noStart = noStart;
}
public String getHealthCheckType() {
if (healthCheckType==null) {
return DeploymentProperties.DEFAULT_HEALTH_CHECK_TYPE;
}
return healthCheckType;
}
public void setHealthCheckType(String healthCheckType) {
this.healthCheckType = healthCheckType;
}
public List<String> getRoutes() {
return routes;
}
public void setRoutes(Collection<String> routes) {
this.routes = routes == null ? ImmutableList.of() : ImmutableList.copyOf(routes);
}
public void setRoutes(String... routes) {
setRoutes(ImmutableList.copyOf(routes));
}
@Override
public String toString() {
return "CFPushArguments [appName=" + appName + ", routes=" + routes + ", memory=" + memory + ", diskQuota="
+ diskQuota + ", timeout=" + timeout + ", buildpack=" + buildpack + ", command=" + command + ", stack="
+ stack + ", env=" + env + ", instances=" + instances + ", services=" + services + ", noStart="
+ noStart + ", healthCheckType="+ healthCheckType+" ]";
}
}

View File

@@ -0,0 +1,73 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client.v2;
import org.cloudfoundry.operations.routes.Route;
public class CFRoute {
final private String domain;
final private String host;
final private String path;
public CFRoute(Route route) {
this.domain = route.getDomain();
this.host = route.getHost();
this.path = route.getPath();
}
public CFRoute(String domain, String host, String path) {
super();
this.domain = domain;
this.host = host;
this.path = path;
}
public String getDomain() {
return domain;
}
public String getHost() {
return host;
}
public String getPath() {
return path;
}
@Override
public String toString() {
return "CFRoute [domain=" + domain + ", host=" + host + ", path=" + path + "]";
}
public static CFRoute.Builder builder() {
return new Builder();
}
public static class Builder {
private String domain;
private String host;
private String path = "";
public CFRoute build() {
return new CFRoute(this.domain, this.host, this.path);
}
public Builder domain(String domain) {
this.domain = domain;
return this;
}
public Builder host(String host) {
this.host = host;
return this;
}
}
}

View File

@@ -0,0 +1,287 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client.v2;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.cloudfoundry.client.v2.buildpacks.BuildpackResource;
import org.cloudfoundry.client.v2.domains.DomainResource;
import org.cloudfoundry.operations.applications.ApplicationDetail;
import org.cloudfoundry.operations.applications.ApplicationSummary;
import org.cloudfoundry.operations.applications.InstanceDetail;
import org.cloudfoundry.operations.organizations.OrganizationSummary;
import org.cloudfoundry.operations.services.ServiceInstance;
import org.cloudfoundry.operations.services.ServiceInstanceType;
import org.cloudfoundry.operations.spaces.SpaceSummary;
import org.cloudfoundry.operations.stacks.Stack;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFAppState;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFApplication;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFApplicationDetail;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFBuildpack;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFCloudDomain;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFInstanceState;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFInstanceStats;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFOrganization;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFServiceInstance;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFSpace;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFStack;
import com.google.common.collect.ImmutableList;
/**
* Various helper methods to 'wrap' objects returned by CF client into
* our own types, so that we do not directly expose library types to our
* code.
*
* @author Kris De Volder
*/
public class CFWrappingV2 {
private static final Logger logger = Logger.getLogger(DefaultClientRequestsV2.class.getName());
public static CFBuildpack wrap(BuildpackResource rsrc) {
String name = rsrc.getEntity().getName();
return new CFBuildpack() {
@Override
public String getName() {
return name;
}
};
}
public static CFApplicationDetail wrap(ApplicationDetail details, ApplicationExtras extras) {
if (details!=null) {
List<CFInstanceStats> instances = ImmutableList.copyOf(
details.getInstanceDetails()
.stream()
.map(CFWrappingV2::wrap)
.collect(Collectors.toList())
);
CFApplicationSummaryData summary = wrapSummary(details, extras);
return new CFApplicationDetailData(
summary,
instances
);
}
return null;
}
public static CFStack wrap(Stack stack) {
if (stack!=null) {
String name = stack.getName();
return new CFStack() {
@Override
public String getName() {
return name;
}
@Override
public String toString() {
return "CFStack("+name+")";
}
};
}
return null;
}
public static CFApplicationDetail wrap(
CFApplicationSummaryData summary,
ApplicationDetail details
) {
List<CFInstanceStats> instanceDetails = ImmutableList.copyOf(
details
.getInstanceDetails()
.stream()
.map(CFWrappingV2::wrap)
.collect(Collectors.toList())
);
return new CFApplicationDetailData(summary, instanceDetails);
}
public static CFCloudDomain wrap(DomainResource domainRsrc) {
if (domainRsrc!=null) {
String name = domainRsrc.getEntity().getName();
return new CFCloudDomain() {
public String getName() {
return name;
}
@Override
public String toString() {
return "CFCloudDomain("+name+")";
}
};
}
return null;
}
public static CFInstanceStats wrap(InstanceDetail instanceDetail) {
return new CFInstanceStats() {
@Override
public CFInstanceState getState() {
try {
return CFInstanceState.valueOf(instanceDetail.getState());
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
return CFInstanceState.UNKNOWN;
}
}
@Override
public String toString() {
return ""+getState();
}
};
}
private static CFApplicationSummaryData wrapSummary(ApplicationDetail app, ApplicationExtras extras) {
CFAppState state;
try {
state = CFAppState.valueOf(app.getRequestedState());
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
state = CFAppState.UNKNOWN;
}
return new CFApplicationSummaryData(
app.getName(),
app.getInstances(),
app.getRunningInstances(),
app.getMemoryLimit(),
UUID.fromString(app.getId()),
app.getUrls(),
state,
app.getDiskQuota(),
extras
);
}
public static CFApplication wrap(ApplicationSummary app, ApplicationExtras extras) {
CFAppState state;
try {
state = CFAppState.valueOf(app.getRequestedState());
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
state = CFAppState.UNKNOWN;
}
return new CFApplicationSummaryData(
app.getName(),
app.getInstances(),
app.getRunningInstances(),
app.getMemoryLimit(),
UUID.fromString(app.getId()),
app.getUrls(),
state,
app.getDiskQuota(),
extras
);
}
public static CFServiceInstance wrap(final ServiceInstance service) {
return new CFServiceInstance() {
@Override
public String getName() {
return service.getName();
}
@Override
public String getPlan() {
return service.getPlan();
}
@Override
public String getDashboardUrl() {
return service.getDashboardUrl();
}
@Override
public String getService() {
if (service.getType()==ServiceInstanceType.USER_PROVIDED) {
return "user-provided";
} else {
return service.getService();
}
}
@Override
public String getDescription() {
return service.getDescription();
}
@Override
public String getDocumentationUrl() {
return service.getDocumentationUrl();
}
};
}
public static CFAppState wrapAppState(String s) {
try {
return CFAppState.valueOf(s);
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
return CFAppState.UNKNOWN;
}
}
public static CFSpace wrap(OrganizationSummary org, SpaceSummary space) {
return new CFSpace() {
@Override
public String getName() {
return space.getName();
}
@Override
public CFOrganization getOrganization() {
return wrap(org);
}
@Override
public UUID getGuid() {
return UUID.fromString(space.getId());
}
@Override
public String toString() {
return "CFSpace("+org.getName()+" / "+getName()+")";
}
};
}
public static CFOrganization wrap(OrganizationSummary org) {
return new CFOrganization() {
@Override
public String getName() {
return org.getName();
}
@Override
public UUID getGuid() {
return UUID.fromString(org.getId());
}
};
}
public static CFBuildpack buildpack(String name) {
return new CFBuildpack() {
@Override
public String getName() {
return name;
}
};
}
}

View File

@@ -0,0 +1,94 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client.v2;
/**
* Manages a set of CancelationTokens.
*
* @author Kris De Volder
*/
public class CancelationTokens {
private String DEBUG = null;
//Note: we don't actually have to keep a set of tokens explicitly.
// The tokens use a 'id' which is incremented on each new token.
//So it is easy to cancel all existing tokens based on a their
//id simply by remembering the 'id' where the cancelation
//occurred. All ids 'older' than the current id are 'canceled'.
/**
* An uncancelable token that can be used by operations that don't
* need cancelation support.
*/
public static final CancelationToken NULL = new CancelationToken() {
@Override
public boolean isCanceled() {
return false;
}
};
private final Object SYNC = CancelationTokens.this;
private int canceledAllBefore = 0;
private int nextId = 0;
public CancelationTokens() {
}
public CancelationTokens(String debug) {
this.DEBUG = debug;
}
public interface CancelationToken {
boolean isCanceled();
}
public synchronized CancelationToken create() {
CancelationToken token = new ManagedToken();
debug("creating cancelation token: "+token);
return token;
}
private class ManagedToken implements CancelationToken {
private int id;
private ManagedToken() {
synchronized (SYNC) {
this.id = nextId++;
}
}
public boolean isCanceled() {
synchronized (SYNC) {
boolean isCanceled = id < canceledAllBefore;
debug("isCanceled? ["+id+"] => "+isCanceled);
return isCanceled;
}
}
@Override
public String toString() {
return "CancelToken("+id+")";
}
}
public synchronized void cancelAll() {
canceledAllBefore = nextId;
debug("CancelationTokens < "+canceledAllBefore+" are Canceled");
}
private void debug(String string) {
if (DEBUG!=null) {
System.out.println(DEBUG+": "+string);
}
}
}

View File

@@ -0,0 +1,85 @@
/*******************************************************************************
* Copyright (c) 2015, 2016 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.vscode.commons.cloudfoundry.client.v2;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFApplication;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFApplicationDetail;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFBuildpack;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFCloudDomain;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFServiceInstance;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFSpace;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFStack;
import org.springframework.ide.vscode.commons.cloudfoundry.client.SshClientSupport;
import org.springframework.ide.vscode.commons.cloudfoundry.client.v2.CancelationTokens.CancelationToken;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public interface ClientRequests {
// /**
// * The actual Rest API version that cloud controller claims to be.
// */
// Version getApiVersion();
//
// /**
// * The minimum version that the CF V2 java client claims to support.
// */
// Version getSupportedApiVersion();
/**
* Returns null if the application does not exist. Throws some kind of Exception if there's any other kind of problem.
*/
CFApplicationDetail getApplication(String appName) throws Exception;
//TODO: consider removing the getXXXSupport method and directly adding the apis that these support
// objects provide.
SshClientSupport getSshClientSupport() throws Exception;
void deleteApplication(String name) throws Exception;
void logout();
List<CFApplication> getApplicationsWithBasicInfo() throws Exception;
List<CFBuildpack> getBuildpacks() throws Exception;
List<CFCloudDomain> getDomains() throws Exception;
List<CFServiceInstance> getServices() throws Exception;
List<CFSpace> getSpaces() throws Exception;
List<CFStack> getStacks() throws Exception;
void restartApplication(String appName, CancelationToken token) throws Exception;
void stopApplication(String appName) throws Exception;
Flux<CFApplicationDetail> getApplicationDetails(List<CFApplication> appsToLookUp) throws Exception;
String getHealthCheck(UUID appGuid) throws Exception;
void setHealthCheck(UUID guid, String hcType) throws Exception;
boolean applicationExists(String appName) throws Exception;
//Removed in V2
//void createApplication(CloudApplicationDeploymentProperties deploymentProperties) throws Exception;
//Added since v2:
void push(CFPushArguments args, CancelationToken cancelationToken) throws Exception;
Map<String, String> getApplicationEnvironment(String appName) throws Exception;
Mono<Void> deleteServiceAsync(String serviceName);
/**
* Gets current value of the client's refresh token. Note that the token is only set once it is known.
* Initially, if a client is created via password auth, then the refreshToken won't be known until
* some operation has been executed.
*
* @return Refresh token if it is already known, null otherwise.
*/
String getRefreshToken();
Mono<String> getUserName();
}

View File

@@ -0,0 +1,250 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client.v2;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.cloudfoundry.client.CloudFoundryClient;
import org.cloudfoundry.reactor.ConnectionContext;
import org.cloudfoundry.reactor.DefaultConnectionContext;
import org.cloudfoundry.reactor.ProxyConfiguration;
import org.cloudfoundry.reactor.TokenProvider;
import org.cloudfoundry.reactor.client.ReactorCloudFoundryClient;
import org.cloudfoundry.reactor.doppler.ReactorDopplerClient;
import org.cloudfoundry.reactor.tokenprovider.OneTimePasscodeTokenProvider;
import org.cloudfoundry.reactor.tokenprovider.PasswordGrantTokenProvider;
import org.cloudfoundry.reactor.tokenprovider.RefreshTokenGrantTokenProvider;
import org.cloudfoundry.reactor.uaa.ReactorUaaClient;
import org.springframework.ide.vscode.commons.cloudfoundry.client.target.CFCredentials;
/**
* TODO: Remove this class when the 'thread leak bug' in V2 client is fixed.
*
* At the moment each time {@link SpringCloudFoundryClient} is create a threadpool
* is created by the client and it is never cleaned up. The only way we have
* to mitigate this leak is to try and create as few clients as possible.
* <p>
* So we have a permanent cache of clients here that is reused.
* <p>
* When the bug is fixed then this should no longer be necessary and we can removed this cache
* and just create the client as needed.
*
* @author Kris De Volder
*/
public class CloudFoundryClientCache {
public class CFClientProvider {
final ConnectionContext connection;
final TokenProvider tokenProvider;
//Note the three client objects below are 'stateless' wrappers and it would be
// fine to recreate as needed instead of store them
final CloudFoundryClient client;
final ReactorUaaClient uaaClient;
final ReactorDopplerClient doppler;
private ProxyConfiguration getProxy(String host) {
// TODO: enable proxy support for vscode. The code below retrieves proxy via Eclipse proxy service thus
// not applicable when this is used outside of Eclipse
// try {
// if (StringUtils.hasText(host)) {
// URL url = new URL("https://"+host);
// // In certain cases, the activator would have stopped and the plugin may
// // no longer be available. Usually onl happens on shutdown.
// BootDashActivator plugin = BootDashActivator.getDefault();
// if (plugin != null) {
// IProxyService proxyService = plugin.getProxyService();
// if (proxyService != null) {
// IProxyData[] selectedProxies = proxyService.select(url.toURI());
//
// // No proxy configured or not found
// if (selectedProxies == null || selectedProxies.length == 0) {
// return null;
// }
//
// IProxyData data = selectedProxies[0];
// int proxyPort = data.getPort();
// String proxyHost = data.getHost();
// String user = data.getUserId();
// String password = data.getPassword();
// if (proxyHost!=null) {
// return ProxyConfiguration.builder()
// .host(proxyHost)
// .port(proxyPort==-1?Optional.empty():Optional.of(proxyPort))
// .username(Optional.ofNullable(user))
// .password(Optional.ofNullable(password))
// .build();
//// return proxyHost != null ? new HttpProxyConfiguration(proxyHost, proxyPort,
//// data.isRequiresAuthentication(), user, password) : null;
// }
// }
// }
// }
// } catch (Exception e) {
// Log.log(e);
// }
return null;
}
public CFClientProvider(Params params) {
long sslTimeout = Long.getLong("sts.bootdash.cf.client.ssl.handshake.timeout", 60); //TODO: make a preference for this?
Optional<Boolean> keepAlive = getBooleanSystemProp("http.keepAlive");
debug("cf client keepAlive = "+keepAlive);
connection = DefaultConnectionContext.builder()
.proxyConfiguration(Optional.ofNullable(getProxy(params.host)))
.apiHost(params.host)
.sslHandshakeTimeout(Duration.ofSeconds(sslTimeout))
.keepAlive(keepAlive)
.skipSslValidation(params.skipSsl)
.build();
tokenProvider = createTokenProvider(params);
client = ReactorCloudFoundryClient.builder()
.connectionContext(connection)
.tokenProvider(tokenProvider)
.build();
uaaClient = ReactorUaaClient.builder()
.connectionContext(connection)
.tokenProvider(tokenProvider)
.build();
doppler = ReactorDopplerClient.builder()
.connectionContext(connection)
.tokenProvider(tokenProvider)
.build();
}
private TokenProvider createTokenProvider(Params params) {
CFCredentials creds = params.credentials;
switch (creds.getType()) {
case PASSWORD:
return PasswordGrantTokenProvider.builder()
.username(params.username)
.password(creds.getSecret())
.build();
case REFRESH_TOKEN:
return RefreshTokenGrantTokenProvider.builder()
.token(creds.getSecret())
.build();
case TEMPORARY_CODE:
return OneTimePasscodeTokenProvider.builder()
.passcode(creds.getSecret())
.build();
default:
throw new IllegalStateException("BUG! Missing switch case?");
}
}
private Optional<Boolean> getBooleanSystemProp(String name) {
String str = System.getProperty(name);
if (str!=null) {
return Optional.of(Boolean.valueOf(str));
}
return Optional.empty();
}
}
private static final boolean DEBUG = true;
private static void debug(String string) {
if (DEBUG) {
System.out.println(string);
}
}
public static class Params {
public final String username;
public final CFCredentials credentials;
public final String host;
public final boolean skipSsl;
public Params(String username, CFCredentials credentials, String host, boolean skipSsl) {
super();
this.username = username;
this.credentials = credentials;
this.host = host;
this.skipSsl = skipSsl;
}
@Override
public String toString() {
return "Params [username=" + username + ", host=" + host + ", skipSsl=" + skipSsl
+ "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((host == null) ? 0 : host.hashCode());
result = prime * result + ((credentials == null) ? 0 : credentials.hashCode());
result = prime * result + (skipSsl ? 1231 : 1237);
result = prime * result + ((username == null) ? 0 : username.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Params other = (Params) obj;
if (host == null) {
if (other.host != null)
return false;
} else if (!host.equals(other.host))
return false;
if (credentials == null) {
if (other.credentials != null)
return false;
} else if (!credentials.equals(other.credentials))
return false;
if (skipSsl != other.skipSsl)
return false;
if (username == null) {
if (other.username != null)
return false;
} else if (!username.equals(other.username))
return false;
return true;
}
}
private Map<Params, CFClientProvider> cache = new HashMap<>();
private int clientCount = 0;
public synchronized CFClientProvider getOrCreate(String username, CFCredentials credentials, String host, boolean skipSsl) {
Params params = new Params(username, credentials, host, skipSsl);
CFClientProvider client = cache.get(params);
if (client==null) {
clientCount++;
debug("Creating client ["+clientCount+"]: "+params);
cache.put(params, client = create(params));
} else {
debug("Reusing client ["+clientCount+"]: "+params);
}
return client;
}
protected CFClientProvider create(Params params) {
return new CFClientProvider(params);
}
}

View File

@@ -0,0 +1,19 @@
/*******************************************************************************
* Copyright (c) 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.vscode.commons.cloudfoundry.client.v2;
import org.springframework.ide.vscode.commons.cloudfoundry.client.target.CFClientParams;
public interface CloudFoundryClientFactory {
ClientRequests getClient(CFClientParams params) throws Exception;
}

View File

@@ -0,0 +1,33 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client.v2;
import org.springframework.ide.vscode.commons.cloudfoundry.client.target.CFClientParams;
public class DefaultCloudFoundryClientFactoryV2 implements CloudFoundryClientFactory {
public static final CloudFoundryClientFactory INSTANCE = new DefaultCloudFoundryClientFactoryV2();
/**
* Use 'INSTANCE' constant instead. This class is a singleton.
*/
private DefaultCloudFoundryClientFactoryV2() {}
private CloudFoundryClientCache cache = new CloudFoundryClientCache();
/* (non-Javadoc)
* @see org.springframework.ide.vscode.commons.cloudfoundry.client.v2.CloudFoundryClientFactory#getClient(org.springframework.ide.vscode.commons.cloudfoundry.client.target.CFClientParams)
*/
@Override
public ClientRequests getClient(CFClientParams params) throws Exception {
return new DefaultClientRequestsV2(cache, params);
}
}

View File

@@ -0,0 +1,53 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client.v2;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Interface for Cloud Foundry application deployment properties
*
* @author Alex Boyko
*
*/
public interface DeploymentProperties {
int DEFAULT_MEMORY = 1024;
int DEFAULT_INSTANCES = 1;
String DEFAULT_HEALTH_CHECK_TYPE = "port";
String getAppName();
int getMemory();
int getDiskQuota();
Integer getTimeout();
String getHealthCheckType();
String getBuildpack();
String getCommand();
String getStack();
Map<String, String> getEnvironmentVariables();
int getInstances();
List<String> getServices();
Set<String> getUris();
}

View File

@@ -0,0 +1,295 @@
/*******************************************************************************
* Copyright (c) 2016 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.vscode.commons.cloudfoundry.client.v2;
import java.io.IOException;
import java.time.Duration;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import org.reactivestreams.Publisher;
import org.springframework.ide.vscode.commons.cloudfoundry.client.v2.CancelationTokens.CancelationToken;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;
/**
* @author Kris De Volder
*/
public class ReactorUtils {
private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(45); // reflects default timeout of Mono.block in reactor 2.x.
public static boolean DUMP_STACK_ON_TIMEOUT = false;
// TODO: uncommented when cancellation is handled in vscode
// /**
// * Convert a {@link CancelationToken} into a Mono that raises
// * an {@link OperationCanceledException} when the token is canceled.
// */
// public static <T> Mono<T> toMono(CancelationToken cancelToken) {
// return Mono.delay(Duration.ofSeconds(1))
// .then((ping) ->
// cancelToken.isCanceled()
// ? Mono.<T>error(new OperationCanceledException())
// : Mono.empty()
// )
// .repeatWhenEmpty((x) -> x);
// }
/**
* Similar to Mono.get but logs a more traceable version of the exception to Eclipse's error
* log before 'rethrowing' it.
* <p>
* This is useful because the actual exception is pretty hard to trace. It doesn't even 'point'
* to the line where 'get' was called.
*/
public static <T> T get(Mono<T> mono) throws Exception {
try {
return mono.block(DEFAULT_TIMEOUT);
} catch (Exception e) {
dumpStacks();
throw new IOException(e);
}
}
/**
* Similar to Mono.get but logs a more traceable version of the exception to Eclipse's error
* log before 'rethrowing' it.
* <p>
* This is useful because the actual exception is pretty hard to trace. It doesn't even 'point'
* to the line where 'get' was called.
*/
public static <T> T get(Duration timeout, CancelationToken cancelationToken, Mono<T> mono) throws Exception {
try {
return mono
// TODO: uncomment when cancellation properly supported in vscode
// Mono
// .first(mono,
// toMono(cancelationToken))
// .otherwise(errorFilter(cancelationToken))
.block(timeout);
} catch (Exception e) {
dumpStacks();
throw new IOException(e);
}
}
public static <T> List<T> get(Duration t, Mono<List<T>> m) throws IOException {
try {
return m.block(t);
} catch (Exception e) {
dumpStacks();
throw new IOException(e);
}
}
private static void dumpStacks() {
if (DUMP_STACK_ON_TIMEOUT) {
System.out.println(getStackDumps().toString());
}
}
/**
* A 'filter' to use as a Mono.otherwise hanlder. It transforms any exception into {@link OperationCanceledException}
* when cancelationToken has been canceled.
*/
private static <T> Function<Throwable, Mono<T>> errorFilter(CancelationToken cancelationToken) {
// return (Throwable e) -> cancelationToken.isCanceled()?Mono.error(new OperationCanceledException()):Mono.error(e);
return (Throwable e) -> Mono.error(e);
}
/**
* Deprecated because this is really the same as Mono.justOrEmpty, so use that instead.
*/
@Deprecated
public static <T> Mono<T> just(T it) {
return it == null ? Mono.empty() : Mono.just(it);
}
/**
* @return A function that can be passed to Mono.otherwise to convert a specific exception type into
* Mono.empty().
*/
public static <T> Function<Throwable, Mono<T>> suppressException(Class<? extends Throwable> exceptionType) {
return (Throwable caught) -> {
if (exceptionType.isAssignableFrom(caught.getClass())) {
return Mono.empty();
} else {
return Mono.error(caught);
}
};
}
/**
* Build a Mono<Void> that executes a given number of Mono<Void> one after the
* other.
*/
@SafeVarargs
public static Mono<Void> sequence(Mono<Void>... tasks) {
Mono<Void> seq = Mono.empty();
for (Mono<Void> t : tasks) {
seq = seq.then(t);
}
return seq;
}
/**
* Execute a bunch of mono in parallel. All monos are executed to completion (rather than canceled early
* when one of them fails)
* <p>
* When at least one operation has failed then, upon completion or failure of the last Mono we guarantee that at least
* one of the exceptions is propagated.
*/
public static Mono<Void> safeMerge(Flux<Mono<Void>> operations, int concurrency) {
AtomicReference<Throwable> failure = new AtomicReference<>(null);
return Flux.merge(
operations
.map((Mono<Void> op) -> {
return op.otherwise((e) -> {
failure.compareAndSet(null, e);
return Mono.empty();
});
}),
concurrency //limit concurrency otherwise troubles (flooding/choking request broker?)
)
.then(() -> {
Throwable error = failure.get();
if (error!=null) {
return Mono.error(error);
} else {
return Mono.empty();
}
});
}
/**
* Attach a timestamp to each element in a Stream
*/
public static <T> Flux<Tuple2<T,Long>> timestamp(Flux<T> stream) {
return stream.map((e) -> Tuples.of(e, System.currentTimeMillis()));
}
/**
* Sorts the elements in a flux in a moving time window. I.e. this assumes element order may be
* scrambled but the scrambling has a certain 'time localilty' to it. So we only need to consider
* sorting of elements that arrive 'close to eachother'.
* <p>
* WARNING: The returned flux is intended for a single subscriber. It only maintains a
* single buffer for sorting stream elements. This buffer is consumed when elements
* are released to any subscriber. Therefore if one subscriber received a element it is gone
* from the buffer and will not be delivered to the other subscribers.
*
* @param stream The stream to be sorted
* @param comparator Compare function to sort with
* @param bufferTime The 'window' of time beyond which we don't need to compare elements.
*/
public static <T> Flux<T> sort(Flux<T> stream, Comparator<T> comparator, Duration bufferTime) {
class SorterAccumulator {
final PriorityQueue<Tuple2<T, Long>> holdingPen = new PriorityQueue<>((Tuple2<T, Long> o1, Tuple2<T, Long> o2) -> {
return comparator.compare(o1.getT1(), o2.getT1());
});
final Flux<T> released = Flux.fromIterable(() -> new Iterator<T>() {
@Override
public boolean hasNext() {
Tuple2<T, Long> nxt;
synchronized (holdingPen) {
nxt = holdingPen.peek();
}
return nxt!=null && isOldEnough(nxt);
}
private boolean isOldEnough(Tuple2<T, Long> nxt) {
long age = System.currentTimeMillis() - nxt.getT2();
return age > bufferTime.toMillis();
}
@Override
public T next() {
synchronized (holdingPen) {
return holdingPen.remove().getT1();
}
}
});
public SorterAccumulator next(Flux<Tuple2<T, Long>> window) {
window.subscribe((e) -> {
synchronized (holdingPen) {
holdingPen.add(e);
}
});
return this;
}
public Flux<T> getReleased() {
return released;
}
public Publisher<? extends T> drain() {
return Flux.fromIterable(holdingPen)
.map(Tuple2::getT1);
}
}
SorterAccumulator sorter = new SorterAccumulator();
return timestamp(stream)
.window(bufferTime)
.scan(sorter, SorterAccumulator::next)
.concatMap(SorterAccumulator::getReleased)
.concatWith(sorter.drain());
}
protected static StringBuffer getStackDumps() {
StringBuffer sb = new StringBuffer();
Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
for (Map.Entry<Thread, StackTraceElement[]> entry : traces.entrySet()) {
sb.append(entry.getKey().toString());
sb.append("\n");
for (StackTraceElement element : entry.getValue()) {
sb.append(" ");
sb.append(element.toString());
sb.append("\n");
}
sb.append("\n");
}
return sb;
}
/**
* Connect a mono to a CompletableFuture so that the result of the mono
* can be retrieved from the {@link CompletableFuture} by calling it's 'get'
* method.
*/
public static <T> void completeWith(CompletableFuture<T> future, Mono<T> mono) {
mono.doOnNext((T v) -> {
future.complete(v);
})
.doOnError((Throwable e) -> {
future.completeExceptionally(e);
})
.subscribeOn(Schedulers.elastic())
.subscribe();
}
}

View File

@@ -0,0 +1,41 @@
/*******************************************************************************
* Copyright (c) 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.vscode.commons.cloudfoundry.client;
import static org.junit.Assert.assertTrue;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.ide.vscode.commons.cloudfoundry.client.target.CFClientParamsFactory;
import org.springframework.ide.vscode.commons.cloudfoundry.client.target.CFClientTarget;
import org.springframework.ide.vscode.commons.cloudfoundry.client.target.CFClientTargets;
import org.springframework.ide.vscode.commons.cloudfoundry.client.v2.CloudFoundryClientFactory;
import org.springframework.ide.vscode.commons.cloudfoundry.client.v2.DefaultCloudFoundryClientFactoryV2;
public class CFClientTest {
@Ignore @Test public void testGetBuildpacksFromCliParamsTarget() throws Exception {
CFClientParamsFactory paramsFactory = CFClientParamsFactory.INSTANCE;
CloudFoundryClientFactory clientFactory = DefaultCloudFoundryClientFactoryV2.INSTANCE;
CFClientTargets targets = new CFClientTargets(paramsFactory, clientFactory);
CFClientTarget target = targets.getTargets().get(0);
List<CFBuildpack> buildPacks = target.getBuildpacks();
assertTrue(!buildPacks.isEmpty());
}
}

View File

@@ -76,4 +76,13 @@ public class ExceptionUtil {
return new ExecutionException(cause);
}
public static Object exception(String message, Throwable error) {
if (message != null) {
// Wrap only if there is an additional message
return new ExecutionException(message, error);
} else {
return exception(error);
}
}
}

View File

@@ -238,6 +238,22 @@ public class Editor {
assertEquals(expect.toString(), actual.toString());
}
public void assertContainsCompletions(String... expectTextAfter) throws Exception {
StringBuilder actual = new StringBuilder();
for (CompletionItem completion : getCompletions()) {
Editor editor = this.clone();
editor.apply(completion);
actual.append(editor.getText());
actual.append("\n-------------------\n");
}
String actualText = actual.toString();
for (String after : expectTextAfter) {
assertContains(after, actualText);
}
}
public void apply(CompletionItem completion) throws Exception {
TextEdit edit = completion.getTextEdit();
String docText = document.getText();

View File

@@ -16,7 +16,7 @@
<module>commons-util</module>
<module>commons-java</module>
<module>java-properties</module>
<module>commons-cf</module>
<module>commons-maven</module>
@@ -45,6 +45,11 @@
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>http://repo.spring.io/release</url>
</repository>
</repositories>
<properties>
@@ -57,7 +62,10 @@
<jackson-2-version>2.5.0</jackson-2-version>
<jersey-2-version>2.10</jersey-2-version>
<lsp4j-version>0.1.0-SNAPSHOT</lsp4j-version>
<reactor-version>3.0.2.RELEASE</reactor-version>
<!-- NOTE: Reactor version must match version used by the CF client -->
<reactor-version>3.0.4.RELEASE</reactor-version>
<reactor-netty>0.6.0.RELEASE</reactor-netty>
<cloudfoundry-client-version>2.1.0.RELEASE</cloudfoundry-client-version>
</properties>
<build>

View File

@@ -42,6 +42,12 @@
<artifactId>commons-yaml</artifactId>
<version>${project.version}</version>
</dependency>
<!-- CF -->
<dependency>
<groupId>org.springframework.ide.vscode</groupId>
<artifactId>commons-cf</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Test harness -->
<dependency>
<groupId>org.springframework.ide.vscode</groupId>

View File

@@ -0,0 +1,69 @@
/*******************************************************************************
* Copyright (c) 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.vscode.manifest.yaml;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Provider;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFBuildpack;
import org.springframework.ide.vscode.commons.cloudfoundry.client.target.CFClientTarget;
import org.springframework.ide.vscode.commons.yaml.schema.BasicYValueHint;
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
public class ManifestYamlCFBuildpacksProvider implements Provider<Collection<YValueHint>> {
private final List<CFClientTarget> targets;
private static final Logger logger = Logger.getLogger(ManifestYamlCFBuildpacksProvider.class.getName());
public ManifestYamlCFBuildpacksProvider(List<CFClientTarget> targets) {
this.targets = targets;
}
@Override
public Collection<YValueHint> get() {
List<YValueHint> hints = new ArrayList<>();
if (targets != null) {
for (CFClientTarget cfClientTarget : targets) {
List<CFBuildpack> buildpacks;
try {
buildpacks = cfClientTarget.getBuildpacks();
if (buildpacks != null) {
for (CFBuildpack buildpack : buildpacks) {
String name = buildpack.getName();
String label = getBuildpackLabel(cfClientTarget, buildpack);
YValueHint hint = new BasicYValueHint(name, label);
if (!hints.contains(hint)) {
hints.add(hint);
}
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
}
}
return hints;
}
protected String getBuildpackLabel(CFClientTarget target, CFBuildpack buildpack) {
return buildpack.getName() + " (" + target.getName() + ")";
}
}

View File

@@ -0,0 +1,67 @@
/*******************************************************************************
* Copyright (c) 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.vscode.manifest.yaml;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Provider;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFServiceInstance;
import org.springframework.ide.vscode.commons.cloudfoundry.client.target.CFClientTarget;
import org.springframework.ide.vscode.commons.yaml.schema.BasicYValueHint;
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
public class ManifestYamlCFServicesProvider implements Provider<Collection<YValueHint>> {
private final List<CFClientTarget> targets;
private static final Logger logger = Logger.getLogger(ManifestYamlCFServicesProvider.class.getName());
public ManifestYamlCFServicesProvider(List<CFClientTarget> targets) {
this.targets = targets;
}
@Override
public Collection<YValueHint> get() {
List<YValueHint> hints = new ArrayList<>();
if (targets != null) {
for (CFClientTarget cfClientTarget : targets) {
try {
List<CFServiceInstance> services = cfClientTarget.getClientRequests().getServices();
if (services != null) {
for (CFServiceInstance service : services) {
String name = service.getName();
String label = getServiceLabel(cfClientTarget, service);
YValueHint hint = new BasicYValueHint(name, label);
if (!hints.contains(hint)) {
hints.add(hint);
}
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
}
}
return hints;
}
private String getServiceLabel(CFClientTarget cfClientTarget, CFServiceInstance service) {
return service.getName() + " - " + service.getPlan() + " (" + cfClientTarget.getParams().getOrgName() + " - "
+ cfClientTarget.getParams().getSpaceName() + ")";
}
}

View File

@@ -1,12 +1,20 @@
package org.springframework.ide.vscode.manifest.yaml;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Provider;
import org.eclipse.lsp4j.CompletionOptions;
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.TextDocumentSyncKind;
import org.springframework.ide.vscode.commons.cloudfoundry.client.target.CFClientParamsFactory;
import org.springframework.ide.vscode.commons.cloudfoundry.client.target.CFClientTarget;
import org.springframework.ide.vscode.commons.cloudfoundry.client.target.CFClientTargets;
import org.springframework.ide.vscode.commons.cloudfoundry.client.v2.CloudFoundryClientFactory;
import org.springframework.ide.vscode.commons.cloudfoundry.client.v2.DefaultCloudFoundryClientFactoryV2;
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngine;
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfoProvider;
@@ -32,16 +40,25 @@ import com.google.common.collect.ImmutableList;
public class ManifestYamlLanguageServer extends SimpleLanguageServer {
private static final Provider<Collection<YValueHint>> NO_BUILDPACKS = () -> ImmutableList.of();
private static final Provider<Collection<YValueHint>> NO_PROVIDER = () -> ImmutableList.of();
private static Logger logger = Logger.getLogger(ManifestYamlLanguageServer.class.getName());
private Yaml yaml = new Yaml();
private YamlSchema schema = new ManifestYmlSchema(NO_BUILDPACKS);
private YamlSchema schema;
private CFClientTargets cfClientTargets;
public ManifestYamlLanguageServer() {
SimpleTextDocumentService documents = getTextDocumentService();
YamlASTProvider parser = new YamlParser(yaml);
CFClientTargets cfTargets = getCFTargets();
Provider<Collection<YValueHint>> buildPacksProvider = getBuildpacksProvider(cfTargets);
Provider<Collection<YValueHint>> servicesProvider = getServicesProvider(cfTargets);
schema = new ManifestYmlSchema(buildPacksProvider, servicesProvider);
YamlStructureProvider structureProvider = YamlStructureProvider.DEFAULT;
YamlAssistContextProvider contextProvider = new SchemaBasedYamlAssistContextProvider(schema);
@@ -72,7 +89,48 @@ public class ManifestYamlLanguageServer extends SimpleLanguageServer {
documents.onCompletionResolve(completionEngine::resolveCompletion);
documents.onHover(hoverEngine ::getHover);
}
private CFClientTargets getCFTargets() {
if (cfClientTargets == null) {
CFClientParamsFactory paramsFactory = CFClientParamsFactory.INSTANCE;
CloudFoundryClientFactory clientFactory = DefaultCloudFoundryClientFactoryV2.INSTANCE;
cfClientTargets = new CFClientTargets(paramsFactory, clientFactory);
}
return cfClientTargets;
}
private Provider<Collection<YValueHint>> getBuildpacksProvider(CFClientTargets targets) {
try {
if (targets != null) {
List<CFClientTarget> cfTargets = targets.getTargets();
if (cfTargets != null && !cfTargets.isEmpty()) {;
return new ManifestYamlCFBuildpacksProvider(cfTargets);
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
return NO_PROVIDER;
}
private Provider<Collection<YValueHint>> getServicesProvider(CFClientTargets targets) {
try {
if (targets != null) {
List<CFClientTarget> cfTargets = targets.getTargets();
if (cfTargets != null && !cfTargets.isEmpty()) {
return new ManifestYamlCFServicesProvider(cfTargets);
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
return null;
}
@Override
protected ServerCapabilities getServerCapabilities() {

View File

@@ -19,6 +19,7 @@ import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.AbstractType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YAtomicType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YTypedPropertyImpl;
@@ -41,7 +42,7 @@ public class ManifestYmlSchema implements YamlSchema {
"name", "host", "hosts"
);
public ManifestYmlSchema(Provider<Collection<YValueHint>> buildpackProvider) {
public ManifestYmlSchema(Provider<Collection<YValueHint>> buildpackProvider, Provider<Collection<YValueHint>> servicesProvider) {
this.buildpackProvider = buildpackProvider;
YTypeFactory f = new YTypeFactory();
TYPE_UTIL = f.TYPE_UTIL;
@@ -51,15 +52,20 @@ public class ManifestYmlSchema implements YamlSchema {
YBeanType application = f.ybean("Application");
YAtomicType t_path = f.yatomic("Path");
YAtomicType t_buildpack = f.yatomic("Buildpack");
YAtomicType t_buildpack = f.yatomic("Buildpack");
t_buildpack.addHintProvider(this.buildpackProvider);
YType t_service_string = f.yatomic("String");
if (servicesProvider != null && t_service_string instanceof AbstractType) {
((AbstractType)t_service_string).addHintProvider(servicesProvider);
}
YType t_services = f.yseq(t_service_string);
YAtomicType t_boolean = f.yenum("boolean", "true", "false");
YType t_string = f.yatomic("String");
YType t_strings = f.yseq(t_string);
YAtomicType t_memory = f.yatomic("Memory");
t_memory.addHints("256M", "512M", "1024M");
t_memory.parseWith(ManifestYmlValueParsers.MEMORY);
@@ -94,7 +100,7 @@ public class ManifestYmlSchema implements YamlSchema {
f.yprop("no-route", t_boolean),
f.yprop("path", t_path),
f.yprop("random-route", t_boolean),
f.yprop("services", t_strings),
f.yprop("services", t_services),
f.yprop("stack", t_string),
f.yprop("timeout", t_pos_integer),
f.yprop("health-check-type", t_health_check_type)

View File

@@ -0,0 +1,63 @@
/*******************************************************************************
* Copyright (c) 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.vscode.manifest.yaml;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.ide.vscode.languageserver.testharness.Editor;
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
public class ManifestYamlEditorCFTest {
LanguageServerHarness harness;
@Before
public void setup() throws Exception {
harness = new LanguageServerHarness(ManifestYamlLanguageServer::new);
harness.intialize(null);
}
/*
* Optional test that is here only to be run in certain conditions. Tests if
* buildpack completion values are actually fetched from PWS if the test env
* also has a CLI that is alread connected to PWS. Enable only if underlying
* conditions for CF connection from vscode are present. For example, CLI is
* installed.
*/
@Ignore
@Test
public void optionalDynamicBuildpacksPWSUsingCliParams() throws Exception {
// No special test harness setup to use CLI. It is a "default" CF client params
// provider in the CF vscode framework.
// Just have to make sure the test env has a CLI that is connected to
// PWS
assertContainsCompletions("buildpack: <*>", "buildpack: java_buildpack<*>");
}
@Ignore
@Test
public void optionalDynamicServicesPWSUsingCliParams() throws Exception {
// No special test harness setup to use CLI. It is a "default" CF client params
// provider in the CF vscode framework.
// Just have to make sure the test env has a CLI that is connected to
// PWS
assertContainsCompletions( "services:\n"+
" - <*>", "sql");
}
//////////////////////////////////////////////////////////////////////////////
private void assertContainsCompletions(String textBefore, String... textAfter) throws Exception {
Editor editor = harness.newEditor(textBefore);
editor.assertContainsCompletions(textAfter);
}
}

View File

@@ -82,7 +82,7 @@ public class ManifestYmlSchemaTest {
"timeout"
};
ManifestYmlSchema schema = new ManifestYmlSchema(null);
ManifestYmlSchema schema = new ManifestYmlSchema(null, null);
@Test
public void toplevelProperties() throws Exception {