/*******************************************************************************
* 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.eclipse.boot.dash.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.springframework.ide.eclipse.boot.dash.test.CloudFoundryTestHarness.APP_DEPLOY_TIMEOUT;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.io.IOUtils;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.junit.After;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.mockito.Mockito;
import org.osgi.framework.Version;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFAppState;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFApplication;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFApplicationDetail;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFBuildpack;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFClientParams;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFCloudDomain;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFServiceInstance;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFSpace;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFStack;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.HealthChecks;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.SshClientSupport;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.SshHost;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.v2.CFPushArguments;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.v2.DefaultClientRequestsV2;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.v2.DefaultCloudFoundryClientFactoryV2;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.v2.ReactorUtils;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.console.IApplicationLogConsole;
import org.springframework.ide.eclipse.boot.dash.model.UserInteractions;
import org.springframework.ide.eclipse.boot.dash.util.CancelationTokens;
import org.springframework.ide.eclipse.boot.test.BootProjectTestHarness;
import org.springframework.ide.eclipse.boot.test.util.TestBracketter;
import org.springframework.ide.eclipse.boot.util.RetryUtil;
import org.springframework.ide.eclipse.boot.util.Thunk;
import org.springframework.ide.eclipse.editor.support.util.StringUtil;
import org.springsource.ide.eclipse.commons.frameworks.test.util.ACondition;
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
import org.springsource.ide.eclipse.commons.tests.util.StsTestUtil;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import junit.framework.AssertionFailedError;
import reactor.core.Cancellation;
import reactor.core.publisher.Flux;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class CloudFoundryClientTest {
public String CFAPPS_IO() {
return get_CFAPPS_IO(clientParams);
}
public static String get_CFAPPS_IO(CFClientParams clientParams) {
String org = clientParams.getOrgName();
String api = clientParams.getApiUrl();
if (org.equals("application-platform-testing")) {
//PWS test space/org
return "cfapps.io";
} else if (org.equals("pivot-kdevolder")) {
//PEZ
return "cfapps.pez.pivotal.io";
} else if (api.contains("api.tan.")) {
//TAN
return "tan.springapps.io";
}
throw new AssertionFailedError("unknown test environment, not sure what to expect here");
}
public String[] getExpectedDomains() {
String org = clientParams.getOrgName();
String api = clientParams.getApiUrl();
if (org.equals("application-platform-testing")) {
//PWS test space/org
return new String[] {
"cfapps.io"
};
} else if (org.equals("pivot-kdevolder")) {
//PEZ
return new String[] {
"cfapps.pez.pivotal.io",
"pezapp.io"
};
} else if (api.contains("api.tan.")) {
//TAN
return new String[] {
"tan.springapps.io"
};
}
throw new AssertionFailedError("unknown test environment, not sure what to expect here");
}
public String[] getExectedBuildpacks() {
String org = clientParams.getOrgName();
String api = clientParams.getApiUrl();
if (org.equals("application-platform-testing")) {
//PWS test space/org
return new String[] {
"staticfile_buildpack",
"java_buildpack",
"ruby_buildpack"
};
} else if (org.equals("pivot-kdevolder")) {
//PEZ
return new String[] {
"staticfile_buildpack",
"java_buildpack_offline",
"ruby_buildpack"
};
} else if (api.contains("api.tan.")) {
//TAN
return new String[] {
"staticfile_buildpack",
"java_buildpack_offline",
"ruby_buildpack"
};
}
throw new AssertionFailedError("unknown test environment, not sure what to expect here");
}
public String getExpectedSshHost() {
String org = clientParams.getOrgName();
String api = clientParams.getApiUrl();
if (org.equals("application-platform-testing")) {
//PWS
return "ssh.run.pivotal.io";
} else if (api.contains("api.tan.")) {
//TAN
return "ssh.tan.springapps.io";
}
throw new AssertionFailedError("unknown test environment, not sure what to expect here");
}
public static final Predicate<Throwable> FLAKY_SERVICE_BROKER = (e) -> {
String msg = ExceptionUtil.getMessage(e).toLowerCase();
return msg.contains("500")
|| msg.contains("502")
;
};
private CFClientParams clientParams = CfTestTargetParams.fromEnv();
private DefaultClientRequestsV2 client = createClient(clientParams);
public TestBracketter bracketer = new TestBracketter();
public CloudFoundryServicesHarness services = new CloudFoundryServicesHarness(clientParams, client);
public CloudFoundryApplicationHarness appHarness = new CloudFoundryApplicationHarness(client);
@Before
public void setup() {
ReactorUtils.DUMP_STACK_ON_TIMEOUT = true;
}
@After
public void teardown() throws Exception {
appHarness.dispose(); //apps first because services still bound to apps can't be deleted!
services.dispose();
if (client!=null) {
client.logout();
}
StsTestUtil.cleanUpProjects();
ReactorUtils.DUMP_STACK_ON_TIMEOUT = false;
}
public BootProjectTestHarness projects = new BootProjectTestHarness(ResourcesPlugin.getWorkspace());
private UserInteractions ui = Mockito.mock(UserInteractions.class);
public static DefaultClientRequestsV2 createClient(CFClientParams params) {
try {
DefaultCloudFoundryClientFactoryV2 factory = DefaultCloudFoundryClientFactoryV2.INSTANCE;
return (DefaultClientRequestsV2) factory.getClient(params);
} catch (Exception e) {
throw new Error(e);
}
}
@Rule
public TestBracketter testBrack = new TestBracketter();
@Test
public void testGetApiVersion() throws Exception {
Version version = client.getApiVersion();
System.out.println("Api version = "+version);
assertNotNull(version);
}
@Test
public void testGetSpaces() throws Exception {
int success = 0;
int failed = 0;
Exception error = null;
for (int i = 0; i < 5; i++) {
try {
long start = System.currentTimeMillis();
List<CFSpace> spaces = client.getSpaces();
long duration = System.currentTimeMillis() - start;
System.out.println("getSpaces -> "+spaces.size()+" spaces in "+ duration + " ms");
success++;
} catch (Exception e) {
error = e;
failed++;
System.out.println("getSpaces -> "+ExceptionUtil.getMessage(e));
}
}
System.out.println("getSpaces failure rate = "+failed + "/" +(success+failed));
if (failed>0) {
throw new IOException("getSpaces failure rate = "+failed + "/" +(success+failed), error);
}
}
@Test
public void testGetApplicationDetails() throws Exception {
String appName = appHarness.randomAppName();
try (CFPushArguments params = new CFPushArguments()) {
params.setAppName(appName);
params.setApplicationData(getTestZip("testapp"));
params.setBuildpack("staticfile_buildpack");
params.setNoStart(true);
push(params);
}
{
CFApplicationDetail appDetails = client.getApplication(appName);
assertEquals(0, appDetails.getRunningInstances());
assertEquals(CFAppState.STOPPED, appDetails.getState());
assertEquals(ImmutableList.of(), appDetails.getInstanceDetails());
}
client.restartApplication(appName, CancelationTokens.NULL);
{
CFApplicationDetail appDetails = client.getApplication(appName);
assertEquals(1, appDetails.getRunningInstances());
assertEquals(CFAppState.STARTED, appDetails.getState());
assertEquals(1, appDetails.getInstanceDetails().size());
}
}
@Test
public void testPushAndBindServices() throws Exception {
//This test fails occasionally because service binding is 'unreliable'. Had a long discussion
// with Ben Hale. The gist is errors happen and should be expected in distributed world.
//They are coming from 'AppDirect' which manages the services. The errors are mediated through cloudfoundry
// which doesn't knwow how it should handle them. So it passed the buck onto the its callers.
//In this case.... cf-java-client which does the same thing and passes them to us.
//All the reasons why they can't handle these errors also apply to us, which means that
//the operation is simply unreliable and so failure is an expected outcome even when everything
//works correctly.
//To avoid this test case from failing too often we retry it a few times.
RetryUtil.retryTimes("testPushAndBindServices", 4, () -> {
System.out.println("Executing full test scenario.");
String service1 = services.createTestService();
String service2 = services.createTestService();
String service3 = services.createTestService(); //An extra unused service (makes this a better test).
String appName = appHarness.randomAppName();
CFPushArguments params = new CFPushArguments();
params.setAppName(appName);
params.setApplicationData(getTestZip("testapp"));
params.setBuildpack("staticfile_buildpack");
params.setServices(ImmutableList.of(service1, service2));
push(params);
assertEquals(ImmutableSet.of(service1, service2), getBoundServiceNames(appName));
client.bindAndUnbindServices(appName, ImmutableList.of(service1)).block();
assertEquals(ImmutableSet.of(service1), getBoundServiceNames(appName));
client.bindAndUnbindServices(appName, ImmutableList.of(service2)).block();
assertEquals(ImmutableSet.of(service2), getBoundServiceNames(appName));
client.bindAndUnbindServices(appName, ImmutableList.of()).block();
assertEquals(ImmutableSet.of(), getBoundServiceNames(appName));
});
}
private Set<String> getBoundServiceNames(String appName) throws Exception {
return client.getBoundServicesSet(appName).block();
}
@Test
public void testPushAndBindHostAndDomain() throws Exception {
String appName = appHarness.randomAppName();
for (int i = 0; i < 2; i++) {
//Why this loop? Because there was bug which CF V2 that made second push fail to bind to
// map a host that was previously mapped.
if (i>0) {
System.out.println("Delete app");
client.deleteApplication(appName);
}
System.out.println("Pushing "+(i+1));
CFPushArguments params = new CFPushArguments();
params.setAppName(appName);
params.setApplicationData(getTestZip("testapp"));
params.setBuildpack("staticfile_buildpack");
params.setRoutes(ImmutableList.of(appName+"."+CFAPPS_IO()));
push(params);
}
System.out.println("Pushing SUCCESS");
CFApplicationDetail app = client.getApplication(appName);
assertNotNull("Expected application to exist after push: " + appName, app);
assertEquals(ImmutableSet.of(appName+"."+CFAPPS_IO()), ImmutableSet.copyOf(app.getUris()));
}
@Test
public void testPushAndBindMultipleHosts() throws Exception {
String[] hostNames = {
appHarness.randomAppName(),
appHarness.randomAppName()
};
String appName = hostNames[0];
CFPushArguments params = new CFPushArguments();
params.setAppName(hostNames[0]);
params.setApplicationData(getTestZip("testapp"));
params.setBuildpack("staticfile_buildpack");
Set<String> routes = ImmutableSet.copyOf(Stream.of(hostNames)
.map((host) -> host + "." + CFAPPS_IO())
.collect(Collectors.toList())
);
params.setRoutes(routes);
push(params);
System.out.println("Pushing SUCCESS");
CFApplicationDetail app = client.getApplication(appName);
assertNotNull("Expected application to exist after push: " + appName, app);
assertEquals(routes, ImmutableSet.copyOf(app.getUris()));
}
@Test
public void testPushAndSetRoutes() throws Exception {
String[] hostNames = {
appHarness.randomAppName(),
appHarness.randomAppName()
};
String appName = hostNames[0];
CFPushArguments params = new CFPushArguments();
params.setAppName(hostNames[0]);
params.setApplicationData(getTestZip("testapp"));
params.setBuildpack("staticfile_buildpack");
Set<String> routes = ImmutableSet.copyOf(Stream.of(hostNames)
.map((host) -> host + "." + CFAPPS_IO())
.collect(Collectors.toList())
);
params.setRoutes(routes);
push(params);
System.out.println("Pushing SUCCESS");
{
CFApplicationDetail app = client.getApplication(appName);
assertNotNull("Expected application to exist after push: " + appName, app);
assertEquals(routes, ImmutableSet.copyOf(app.getUris()));
}
doSetRoutesTest(appName, ImmutableSet.of(), params.getRandomRoute());
for (String route : routes) {
doSetRoutesTest(appName, ImmutableSet.of(route), params.getRandomRoute());
}
}
private void doSetRoutesTest(String appName, ImmutableSet<String> routes, boolean randomRoute) throws Exception {
ReactorUtils.get(client.setRoutes(appName, routes, randomRoute));
CFApplicationDetail app = client.getApplication(appName);
assertEquals(routes, ImmutableSet.copyOf(app.getUris()));
}
@Test
public void testPushWithHealthcheckNone() throws Exception {
//This test is to make sure that hc info is properly passed on by push operation
// to the 'real' cf client.
//Since the push works different on firt push and repush we have to check both!
String appName = appHarness.randomAppName();
// First push
try (CFPushArguments params = new CFPushArguments()) {
params.setAppName(appName);
params.setRoutes(appName+"."+CFAPPS_IO());
params.setApplicationData(getTestZip("testapp"));
params.setBuildpack("staticfile_buildpack");
params.setHealthCheckType(HealthChecks.HC_NONE);
push(params);
CFApplicationDetail app = client.getApplication(appName);
assertNotNull("Expected application to exist after push: " + appName, app);
assertEquals(HealthChecks.HC_NONE, app.getHealthCheckType());
}
// re-push
try (CFPushArguments params = new CFPushArguments()) {
params.setAppName(appName);
params.setRoutes(appName+"."+CFAPPS_IO());
params.setApplicationData(getTestZip("testapp"));
params.setBuildpack("staticfile_buildpack");
params.setHealthCheckType(HealthChecks.HC_PORT);
push(params);
CFApplicationDetail app = client.getApplication(appName);
assertNotNull("Expected application to exist after push: " + appName, app);
assertEquals(HealthChecks.HC_PORT, app.getHealthCheckType());
}
}
@Test
public void testPushAndSetEnv() throws Exception {
String appName = appHarness.randomAppName();
CFPushArguments params = new CFPushArguments();
params.setAppName(appName);
params.setRoutes(appName+"."+CFAPPS_IO());
params.setApplicationData(getTestZip("testapp"));
params.setBuildpack("staticfile_buildpack");
params.setEnv(ImmutableMap.of(
"foo", "foo_value",
"bar", "bar_value"
));
push(params);
CFApplicationDetail app = client.getApplication(appName);
assertNotNull("Expected application to exist after push: " + appName, app);
ACondition.waitFor("app content to be availabe", 10_000, () -> {
String content = IOUtils.toString(new URI("http://" + appName + '.' + CFAPPS_IO() + "/test.txt"));
assertTrue(content.length() > 0);
assertTrue(content.contains("content"));
});
{
Map<String, String> env = client.getEnv(appName).block();
assertEquals("foo_value", env.get("foo"));
assertEquals("bar_value", env.get("bar"));
assertEquals(2, env.size());
}
client.setEnvVars(appName, ImmutableMap.of("other", "value")).block();
{
Map<String, String> env = client.getEnv(appName).block();
assertEquals("value", env.get("other"));
assertEquals(1, env.size());
}
//This last piece is commented because it fails.
//See: https://www.pivotaltracker.com/story/show/116804259
// The last var doesn't get removed. Not sure how to fix it.
// But eventually we won't even be using 'setEnvVars' it will be part of the push.
// and its not going to be our problem to fix that.
// client.updateApplicationEnvironment(appName, ImmutableMap.of()).get();
// {
// Map<String, Object> env = client.getEnv(appName).get();
// assertEquals(0, env.size());
// }
}
@Test
public void testDeleteApplication() throws Exception {
String appName = appHarness.randomAppName();
CFPushArguments params = new CFPushArguments();
params.setAppName(appName);
params.setApplicationData(getTestZip("testapp"));
params.setBuildpack("staticfile_buildpack");
push(params);
CFApplicationDetail app = client.getApplication(appName);
assertTrue(client.applicationExists(appName));
assertNotNull("Expected application to exist after push: " + appName, app);
client.deleteApplication(appName);
app = client.getApplication(appName);
assertNull("Expected application to be deleted after delete: " + appName, app);
assertFalse(client.applicationExists(appName));
}
@Test
public void testStopApplication() throws Exception {
String appName = appHarness.randomAppName();
CFPushArguments params = new CFPushArguments();
params.setAppName(appName);
params.setApplicationData(getTestZip("testapp"));
params.setBuildpack("staticfile_buildpack");
push(params);
final CFApplicationDetail runningApp = client.getApplication(appName);
assertNotNull("Expected application to exist after push: " + appName, runningApp);
new ACondition("wait for app '"+ appName +"'to be RUNNING", APP_DEPLOY_TIMEOUT) {
public boolean test() throws Exception {
assertAppRunState(1, runningApp.getRunningInstances(), CFAppState.STARTED, runningApp.getState());
return true;
}
};
client.stopApplication(appName);
final CFApplicationDetail stoppedApp = client.getApplication(appName);
new ACondition("wait for app '"+ appName +"'to be STOPPED", APP_DEPLOY_TIMEOUT) {
public boolean test() throws Exception {
assertAppRunState(0, stoppedApp.getRunningInstances(), CFAppState.STOPPED, stoppedApp.getState());
return true;
}
};
}
@Test
public void getServices() throws Exception {
RetryUtil.retryWhen("getServices", 5, FLAKY_SERVICE_BROKER,
() -> {
String[] serviceNames = new String[3];
Set<String> userProvided = new HashSet<>();
for (int i = 0; i < serviceNames.length; i++) {
if (i%2==0) {
serviceNames[i] = services.createTestService();
} else {
serviceNames[i] = services.createTestUserProvidedService();
userProvided.add(serviceNames[i]);
}
}
List<CFServiceInstance> actualServices = client.getServices();
ImmutableSet<String> actualServiceNames = ImmutableSet.copyOf(
actualServices.stream()
.map(CFServiceInstance::getName)
.collect(Collectors.toList())
);
for (CFServiceInstance s : actualServices) {
//Note: because these test on CI host run in parallel with others using same space...
// we should assume there might be 'extra stuff' on the space than what we just created here.
//So only check that 'our stuff' exists and looks right and ignore the rest.
String name = s.getName();
if (ImmutableSet.copyOf(serviceNames).contains(name)) {
System.out.println("Verifying service: "+name);
if (userProvided.contains(name)) {
assertEquals("user-provided", s.getService());
System.out.println(" user provided => OK");
} else {
assertEquals(services.getTestServiceAndPlan()[0], s.getService());
System.out.println(" getService() => OK");
String expectPlan = services.getTestServiceAndPlan()[1];
assertEquals(expectPlan, s.getPlan());
System.out.println(" getPlan() => OK");
assertIsURL(s.getDashboardUrl());
System.out.println(" getDashboardUrl() => OK");
assertIsURL(s.getDocumentationUrl());
System.out.println(" getDocumentationUrl() => OK");
assertText(s.getDescription());
System.out.println(" getDescription() => OK");
}
}
}
for (String s : serviceNames) {
assertTrue(s+" not found in "+actualServiceNames, actualServiceNames.contains(s));
}
}
);
}
@Test
public void testServiceCreateAndDelete() throws Exception {
RetryUtil.retryWhen("testServiceCreateAndDelete", 5, FLAKY_SERVICE_BROKER, () -> {
String[] serviceNames = new String[2];
for (int i = 0; i < serviceNames.length; i++) {
serviceNames[i] = services.randomServiceName();
};
for (int i = 0; i < serviceNames.length; i++) {
String serviceName = serviceNames[i];
if (i%2==0) {
System.out.println("Create service: "+serviceName);
String[] serviceInfo = services.getTestServiceAndPlan();
client.createService(serviceName, serviceInfo[0], serviceInfo[1])
.block(CloudFoundryServicesHarness.CREATE_SERVICE_TIMEOUT);
} else {
System.out.println("Create user-provided service: "+serviceName);
client.createUserProvidedService(serviceName, ImmutableMap.of())
.block();
}
}
List<CFServiceInstance> services = client.getServices();
assertServices(services, serviceNames);
for (String serviceName : serviceNames) {
client.deleteServiceAsync(serviceName).block();
System.out.println("Deleted service: "+serviceName);
}
assertNoServices(client.getServices(), serviceNames);
});
}
@Test
public void testGetBoundServices() throws Exception {
RetryUtil.retryWhen("testGetBoundServices", 5, FLAKY_SERVICE_BROKER, () -> {
String service1 = services.createTestService();
String service2 = services.createTestService();
String service3 = services.createTestService();
String appName = appHarness.randomAppName();
CFPushArguments params = new CFPushArguments();
params.setAppName(appName);
params.setApplicationData(getTestZip("testapp"));
params.setBuildpack("staticfile_buildpack");
params.setServices(ImmutableList.of(service1, service2));
push(params);
List<CFApplication> allApps = client.getApplicationsWithBasicInfo();
CFApplication app = null;
for (CFApplication a : allApps) {
if (a.getName().equals(appName)) {
app = a;
}
}
assertEquals(ImmutableSet.of(service1, service2), ImmutableSet.copyOf(app.getServices()));
app = client.getApplication(appName);
assertEquals(ImmutableSet.of(service1, service2), ImmutableSet.copyOf(app.getServices()));
});
}
@Test
public void testGetDomains() throws Exception {
CFClientParams params = CfTestTargetParams.fromEnv();
client = createClient(params);
List<CFCloudDomain> domains = client.getDomains();
assertEquals(CFAPPS_IO(), domains.get(0).getName());
Set<String> names = Flux.fromIterable(domains)
.map(CFCloudDomain::getName)
.collectList()
.map(ImmutableSet::copyOf)
.block();
assertContains(names, getExpectedDomains());
}
@Test
public void testGetBuildpacks() throws Exception {
client = createClient(CfTestTargetParams.fromEnv());
List<CFBuildpack> buildpacks = client.getBuildpacks();
Set<String> names = Flux.fromIterable(buildpacks)
.map(CFBuildpack::getName)
.collectList()
.map(ImmutableSet::copyOf)
.block();
assertContains(names, getExectedBuildpacks());
}
@Test
public void testGetStacks() throws Exception {
client = createClient(CfTestTargetParams.fromEnv());
List<CFStack> stacks = client.getStacks();
Set<String> names = Flux.fromIterable(stacks)
.map(CFStack::getName)
.collectList()
.map(ImmutableSet::copyOf)
.block();
assertContains(names,
"cflinuxfs2"
);
}
@Test
public void testApplicationLogConnection() throws Exception {
client = createClient(CfTestTargetParams.fromEnv());
String appName = appHarness.randomAppName();
IApplicationLogConsole listener = mock(IApplicationLogConsole.class);
Cancellation token = client.streamLogs(appName, listener);
assertNotNull(token);
Future<Void> pushResult = doAsync(() -> {
CFPushArguments params = new CFPushArguments();
params.setAppName(appName);
params.setApplicationData(getTestZip("testapp"));
params.setBuildpack("staticfile_buildpack");
push(params);
});
ACondition.waitFor("push", TimeUnit.MINUTES.toMillis(4), () -> {
assertTrue(pushResult.isDone());
});
pushResult.get();
BootDashModelTest.waitForJobsToComplete();
verify(listener, atLeastOnce()).onMessage(any());
}
// @Test
// public void testGetExistingRoutes() throws Exception {
// String appName = "foo";//appHarness.randomAppName();
//
//// CFPushArguments params = new CFPushArguments();
//// params.setAppName(appName);
//// params.setApplicationData(getTestZip("testapp"));
//// params.setBuildpack("staticfile_buildpack");
//// push(params);
//
// for (int i = 0; i < 3; i++) {
// System.out.println("====================");
// assertTrue(client.getExistingRoutes(appName).toList().get().isEmpty());
// }
// }
@Test
public void testGetApplicationBuildpack() throws Exception {
String appName = appHarness.randomAppName();
CFPushArguments params = new CFPushArguments();
params.setAppName(appName);
params.setApplicationData(getTestZip("testapp"));
params.setBuildpack("staticfile_buildpack");
push(params);
//Note we try to get the app two different ways because retrieving the info in
// each case is slightly different.
{
CFApplicationDetail app = client.getApplication(appName);
assertEquals("staticfile_buildpack", app.getBuildpackUrl());
}
{
List<CFApplication> allApps = client.getApplicationsWithBasicInfo();
CFApplication app = null;
for (CFApplication a : allApps) {
if (a.getName().equals(appName)) {
app = a;
}
}
assertEquals("staticfile_buildpack", app.getBuildpackUrl());
}
}
@Test
public void testGetApplicationStack() throws Exception {
String appName = appHarness.randomAppName();
String stackName = "cflinuxfs2";
CFPushArguments params = new CFPushArguments();
params.setAppName(appName);
params.setApplicationData(getTestZip("testapp"));
params.setBuildpack("staticfile_buildpack");
params.setStack(stackName);
push(params);
//Note we try to get the app two different ways because retrieving the info in
// each case is slightly different.
{
CFApplicationDetail app = client.getApplication(appName);
assertEquals(stackName, app.getStack());
}
{
List<CFApplication> allApps = client.getApplicationsWithBasicInfo();
CFApplication app = null;
for (CFApplication a : allApps) {
if (a.getName().equals(appName)) {
app = a;
}
}
assertEquals(stackName, app.getStack());
}
}
@Test
public void testGetApplicationTimeout() throws Exception {
String appName = appHarness.randomAppName();
int timeout = 67;
CFPushArguments params = new CFPushArguments();
params.setAppName(appName);
params.setApplicationData(getTestZip("testapp"));
params.setBuildpack("staticfile_buildpack");
params.setTimeout(timeout);
push(params);
//Note we try to get the app two different ways because retrieving the info in
// each case is slightly different.
{
CFApplicationDetail app = client.getApplication(appName);
assertEquals(timeout, (int)app.getTimeout());
}
{
List<CFApplication> allApps = client.getApplicationsWithBasicInfo();
CFApplication app = null;
for (CFApplication a : allApps) {
if (a.getName().equals(appName)) {
app = a;
}
}
assertEquals(timeout, (int)app.getTimeout());
}
}
@Test
public void testGetApplicationCommand() throws Exception {
String appName = appHarness.randomAppName();
String command = "something interesting";
CFPushArguments params = new CFPushArguments();
params.setAppName(appName);
params.setApplicationData(getTestZip("testapp"));
params.setBuildpack("staticfile_buildpack");
params.setCommand(command);
params.setNoStart(true); // Our command is bogus so starting won't work
push(params);
//Note we try to get the app two different ways because retrieving the info in
// each case is slightly different.
{
CFApplicationDetail app = client.getApplication(appName);
assertEquals(command, app.getCommand());
}
{
List<CFApplication> allApps = client.getApplicationsWithBasicInfo();
CFApplication app = null;
for (CFApplication a : allApps) {
if (a.getName().equals(appName)) {
app = a;
}
}
assertEquals(command, app.getCommand());
}
}
@Test public void testSshSupport() throws Exception {
String appName = appHarness.randomAppName();
CFPushArguments params = new CFPushArguments();
params.setAppName(appName);
params.setApplicationData(getTestZip("testapp"));
params.setBuildpack("staticfile_buildpack");
push(params);
SshClientSupport sshSupport = client.getSshClientSupport();
SshHost sshHost = sshSupport.getSshHost();
System.out.println(sshHost);
assertEquals(getExpectedSshHost(), sshHost.getHost());
assertEquals(2222, sshHost.getPort());
assertTrue(StringUtil.hasText(sshHost.getFingerPrint()));
assertTrue(StringUtil.hasText(sshSupport.getSshCode()));
UUID appGuid = client.getApplication(appName).getGuid();
String sshUser = sshSupport.getSshUser(appGuid, 0);
System.out.println("sshUser = "+sshUser);
assertTrue(StringUtil.hasText(sshUser));
String code = sshSupport.getSshCode();
System.out.println("sshCode = "+code);
assertTrue(StringUtil.hasText(code));
}
@Test public void testGetServiceDashboardUrl() throws Exception {
String serviceName = services.createTestService();
CFServiceInstance service = null;
for (CFServiceInstance s : client.getServices()) {
if (s.getName().equals(serviceName)) {
service = s;
}
}
String dashUrl = service.getDashboardUrl();
assertNotNull(dashUrl);
assertTrue(dashUrl.startsWith("http"));
}
@Test public void startCanBeCanceled() throws Exception {
IProject project = projects.createBootWebProject("slow-starter");
File jarFile = BootJarPackagingTest.packageAsJar(project, ui);
String appName = appHarness.randomAppName();
try (CFPushArguments params = new CFPushArguments()) {
params.setAppName(appName);
params.setRoutes(appName+"."+CFAPPS_IO());
params.setApplicationData(jarFile);
params.setNoStart(true);
long starting = System.currentTimeMillis();
System.out.println("Pushing...");
client.push(params, CancelationTokens.NULL);
long duration = System.currentTimeMillis() - starting;
System.out.println("Pushing took: "+duration+ " ms");
}
CancelationTokens cancelationTokens = new CancelationTokens();
long starting = System.currentTimeMillis();
System.out.println("Starting...");
Future<Void> startResult = doAsync(() -> {
client.restartApplication(appName, cancelationTokens.create());
long duration = System.currentTimeMillis() - starting;
System.out.println("started in "+duration+" ms");
});
Thread.sleep(5000);
long cancelTime = System.currentTimeMillis();
cancelationTokens.cancelAll();
try {
startResult.get(5, TimeUnit.SECONDS);
} catch (ExecutionException e) {
e.printStackTrace();
long duration = System.currentTimeMillis() - cancelTime;
assertEquals(OperationCanceledException.class, ExceptionUtil.getDeepestCause(e).getClass());
System.out.println("\nRestart Canceled after "+duration+" ms");
}
}
@Test public void pushCanBeCanceled() throws Exception {
String appName = appHarness.randomAppName();
IProject project = projects.createBootWebProject("slow-starter");
File jarFile = BootJarPackagingTest.packageAsJar(project, ui);
CancelationTokens cancelationTokens = new CancelationTokens();
try (CFPushArguments params = new CFPushArguments()) {
params.setAppName(appName);
params.setRoutes(appName+"."+CFAPPS_IO());
params.setApplicationData(jarFile);
long starting = System.currentTimeMillis();
Future<Void> pushResult = doAsync(() -> {
System.out.println("Pushing...");
client.push(params, cancelationTokens.create());
long duration = System.currentTimeMillis() - starting;
System.out.println("Pushing took: "+duration+ " ms");
});
Thread.sleep(Duration.ofSeconds(10).toMillis());
long cancelTime = System.currentTimeMillis();
System.out.println("Canceling...");
cancelationTokens.cancelAll();
try {
pushResult.get(5, TimeUnit.SECONDS); // Cancel should happen pretty 'fast'!
fail("push completed but it should have been canceled");
} catch (ExecutionException e) { // real exception is wrapped in EE by Future.get
e.printStackTrace();
long duration = System.currentTimeMillis() - cancelTime;
assertEquals(OperationCanceledException.class, ExceptionUtil.getDeepestCause(e).getClass());
System.out.println("\nPush Canceled after: "+duration +" ms");
}
}
}
/////////////////////////////////////////////////////////////////////////////
private void assertText(String s) {
if (!StringUtil.hasText(s)) {
fail("Found no text, but expected some");
}
}
private void assertIsURL(String url) throws URISyntaxException {
assertText(url);
new URI(url); //parse it
}
private Future<Void> doAsync(Thunk task) {
CompletableFuture<Void> result = new CompletableFuture<>();
Job job = new Job("Async task") {
protected IStatus run(IProgressMonitor monitor) {
try {
task.call();
result.complete(null);
} catch (Throwable e) {
result.completeExceptionally(e);
}
return Status.OK_STATUS;
}
};
job.schedule();
return result;
}
private void push(CFPushArguments _params) throws Exception {
try (CFPushArguments params = _params) {
client.push(params, CancelationTokens.NULL);
}
}
private void assertContains(Set<String> strings, String... expecteds) {
for (String e : expecteds) {
assertContains(e, strings);
}
}
private void assertContains(String expected, Set<String> names) {
assertTrue("Expected '"+expected+"' not found in: "+names, names.contains(expected));
}
private void assertNoServices(List<CFServiceInstance> services, String... serviceNames) throws Exception {
Set<String> names = services.stream().map(CFServiceInstance::getName).collect(Collectors.toSet());
for (String serviceName : serviceNames) {
assertFalse("Service exists but shouldn't: "+serviceName, names.contains(serviceName));
}
}
private void assertServices(List<CFServiceInstance> services, String... serviceNames) throws Exception {
Set<String> names = services.stream().map(CFServiceInstance::getName).collect(Collectors.toSet());
assertContains(names, serviceNames);
}
private void assertAppRunState(int expectedInstances, int actualInstances, CFAppState expectedRequestedState, CFAppState actualRequestedState) {
assertEquals("Expected running instances does not match actual running instances: ", expectedInstances, actualInstances);
assertEquals("Expected requested app state does not match actual requested app state: ", expectedRequestedState, actualRequestedState);
}
private File getTestZip(String fileName) {
File sourceWorkspace = new File(
StsTestUtil.getSourceWorkspacePath("org.springframework.ide.eclipse.boot.dash.test"));
File file = new File(sourceWorkspace, fileName + ".zip");
Assert.isTrue(file.exists(), ""+ file);
return file;
}
}