http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java ---------------------------------------------------------------------- diff --git a/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java b/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java new file mode 100644 index 0000000..2ddad9d --- /dev/null +++ b/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.cli.lister; + +import java.io.File; +import java.lang.annotation.Annotation; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.reflections.Reflections; +import org.reflections.scanners.FieldAnnotationsScanner; +import org.reflections.scanners.SubTypesScanner; +import org.reflections.scanners.TypeAnnotationsScanner; +import org.reflections.util.ConfigurationBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.basic.BrooklynObject; +import brooklyn.enricher.basic.AbstractEnricher; +import brooklyn.entity.Application; +import brooklyn.entity.Entity; +import brooklyn.entity.basic.AbstractApplication; +import brooklyn.entity.basic.AbstractEntity; +import brooklyn.entity.basic.SoftwareProcessImpl; +import brooklyn.policy.Enricher; +import brooklyn.policy.Policy; +import brooklyn.policy.basic.AbstractPolicy; +import brooklyn.util.ResourceUtils; +import brooklyn.util.javalang.UrlClassLoader; +import brooklyn.util.net.Urls; +import brooklyn.util.os.Os; + +import com.google.common.annotations.Beta; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +public class ClassFinder { + + private static final Logger log = LoggerFactory.getLogger(ClassFinder.class); + + private static final Collection<Class<?>> BORING = ImmutableList.<Class<?>>of( + Entity.class, + AbstractEntity.class, + SoftwareProcessImpl.class, + Application.class, + AbstractApplication.class, + Policy.class, + Enricher.class, + AbstractPolicy.class, + AbstractEnricher.class); + + public static Predicate<Class<?>> notBoring() { + return new Predicate<Class<?>>() { + public boolean apply(Class<?> input) { + return (input != null && !BORING.contains(input)); + } + }; + } + + public static Predicate<Class<?>> withAnnotation(final Class<? extends Annotation> annotation) { + return new Predicate<Class<?>>() { + public boolean apply(Class<?> input) { + return (input != null && input.getAnnotation(annotation) != null); + } + }; + } + + public static Predicate<? super Class<? extends BrooklynObject>> withClassNameMatching(final String typeRegex) { + return new Predicate<Class<?>>() { + public boolean apply(Class<?> input) { + return (input != null && input.getName() != null && input.getName().matches(typeRegex)); + } + }; + } + + /** finds a jar at a url, or for directories, jars under a path */ + @Beta + public static List<URL> toJarUrls(String url) throws MalformedURLException { + if (url==null) throw new NullPointerException("Cannot read from null"); + if (url=="") throw new NullPointerException("Cannot read from empty string"); + + List<URL> result = Lists.newArrayList(); + + String protocol = Urls.getProtocol(url); + if (protocol!=null) { + // it's a URL - easy + if ("file".equals(protocol)) { + url = ResourceUtils.tidyFileUrl(url); + } + result.add(new URL(url)); + } else { + // treat as file + String tidiedPath = Os.tidyPath(url); + File tidiedFile = new File(tidiedPath); + if (tidiedFile.isDirectory()) { + List<File> toscan = Lists.newLinkedList(); + toscan.add(tidiedFile); + while (toscan.size() > 0) { + File file = toscan.remove(0); + if (file.isFile()) { + if (file.getName().toLowerCase().endsWith(".jar")) { + result.add(new URL("file://"+file.getAbsolutePath())); + } + } else if (file.isDirectory()) { + for (File subfile : file.listFiles()) { + toscan.add(subfile); + } + } else { + log.info("Cannot read "+file+"; not a file or directory"); + } + } + } else { + result.add(tidiedFile.toURI().toURL()); + } + } + + return result; + } + + public static <T extends BrooklynObject> Set<Class<? extends T>> findClasses(Collection<URL> urls, Class<T> clazz) { + ClassLoader classLoader = new UrlClassLoader(urls.toArray(new URL[urls.size()])); + + Reflections reflections = new ConfigurationBuilder() + .addClassLoader(classLoader) + .addScanners(new SubTypesScanner(), new TypeAnnotationsScanner(), new FieldAnnotationsScanner()) + .addUrls(urls) + .build(); + + Set<Class<? extends T>> types = reflections.getSubTypesOf(clazz); + + return types; + } +}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ItemDescriptors.java ---------------------------------------------------------------------- diff --git a/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ItemDescriptors.java b/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ItemDescriptors.java new file mode 100644 index 0000000..3912dd9 --- /dev/null +++ b/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ItemDescriptors.java @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.cli.lister; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.basic.BrooklynDynamicType; +import brooklyn.basic.BrooklynObject; +import brooklyn.basic.BrooklynType; +import brooklyn.basic.BrooklynTypes; +import org.apache.brooklyn.catalog.Catalog; +import brooklyn.config.ConfigKey; +import brooklyn.entity.Effector; +import brooklyn.entity.EntityType; +import brooklyn.entity.basic.BrooklynConfigKeys; +import brooklyn.event.Sensor; +import brooklyn.location.LocationResolver; +import brooklyn.rest.domain.EffectorSummary; +import brooklyn.rest.domain.EntityConfigSummary; +import brooklyn.rest.domain.SensorSummary; +import brooklyn.rest.domain.SummaryComparators; +import brooklyn.rest.transform.EffectorTransformer; +import brooklyn.rest.transform.EntityTransformer; +import brooklyn.rest.transform.SensorTransformer; +import brooklyn.util.exceptions.RuntimeInterruptedException; + +import com.google.common.base.Strings; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +public class ItemDescriptors { + + private static final Logger LOG = LoggerFactory.getLogger(ItemDescriptors.class); + + public static List<Map<String, Object>> toItemDescriptors(Iterable<? extends Class<? extends BrooklynObject>> types, boolean headingsOnly) { + return toItemDescriptors(types, headingsOnly, null); + } + + public static List<Map<String, Object>> toItemDescriptors(Iterable<? extends Class<? extends BrooklynObject>> types, boolean headingsOnly, final String sortField) { + List<Map<String, Object>> itemDescriptors = Lists.newArrayList(); + + for (Class<? extends BrooklynObject> type : types) { + try { + Map<String, Object> itemDescriptor = toItemDescriptor(type, headingsOnly); + itemDescriptors.add(itemDescriptor); + } catch (Throwable throwable) { + if (throwable instanceof InterruptedException) + throw new RuntimeInterruptedException((InterruptedException) throwable); + if (throwable instanceof RuntimeInterruptedException) + throw (RuntimeInterruptedException) throwable; + + LOG.warn("Could not load "+type+": "+throwable); + } + } + + if (!Strings.isNullOrEmpty(sortField)) { + Collections.sort(itemDescriptors, new Comparator<Map<String, Object>>() { + @Override public int compare(Map<String, Object> id1, Map<String, Object> id2) { + Object o1 = id1.get(sortField); + Object o2 = id2.get(sortField); + if (o1 == null) { + return o2 == null ? 0 : 1; + } + if (o2 == null) { + return -1; + } + return o1.toString().compareTo(o2.toString()); + } + }); + } + + return itemDescriptors; + } + + public static Map<String,Object> toItemDescriptor(Class<? extends BrooklynObject> clazz, boolean headingsOnly) { + BrooklynDynamicType<?, ?> dynamicType = BrooklynTypes.getDefinedBrooklynType(clazz); + BrooklynType type = dynamicType.getSnapshot(); + ConfigKey<?> version = dynamicType.getConfigKey(BrooklynConfigKeys.SUGGESTED_VERSION.getName()); + + Map<String,Object> result = Maps.newLinkedHashMap(); + + result.put("type", clazz.getName()); + if (version != null) { + result.put("defaultVersion", version.getDefaultValue()); + } + + Catalog catalogAnnotation = clazz.getAnnotation(Catalog.class); + if (catalogAnnotation != null) { + result.put("name", catalogAnnotation.name()); + result.put("description", catalogAnnotation.description()); + result.put("iconUrl", catalogAnnotation.iconUrl()); + } + + Deprecated deprecatedAnnotation = clazz.getAnnotation(Deprecated.class); + if (deprecatedAnnotation != null) { + result.put("deprecated", true); + } + + if (!headingsOnly) { + Set<EntityConfigSummary> config = Sets.newTreeSet(SummaryComparators.nameComparator()); + Set<SensorSummary> sensors = Sets.newTreeSet(SummaryComparators.nameComparator()); + Set<EffectorSummary> effectors = Sets.newTreeSet(SummaryComparators.nameComparator()); + + for (ConfigKey<?> x: type.getConfigKeys()) { + config.add(EntityTransformer.entityConfigSummary(x, dynamicType.getConfigKeyField(x.getName()))); + } + result.put("config", config); + + if (type instanceof EntityType) { + for (Sensor<?> x: ((EntityType)type).getSensors()) + sensors.add(SensorTransformer.sensorSummaryForCatalog(x)); + result.put("sensors", sensors); + + for (Effector<?> x: ((EntityType)type).getEffectors()) + effectors.add(EffectorTransformer.effectorSummaryForCatalog(x)); + result.put("effectors", effectors); + } + } + + return result; + } + + public static Object toItemDescriptors(List<LocationResolver> resolvers) { + return toItemDescriptors(resolvers, false); + } + + public static Object toItemDescriptors(List<LocationResolver> resolvers, Boolean sort) { + List<Object> result = Lists.newArrayList(); + for (LocationResolver resolver : resolvers) { + result.add(toItemDescriptor(resolver)); + } + if (sort) { + Collections.sort(result, new Comparator<Object>() { + @Override public int compare(Object o1, Object o2) { + String s1 = o1 == null ? "" : o1.toString(); + String s2 = o2 == null ? "" : o2.toString(); + return s1.compareTo(s2); + } + }); + } + return result; + } + + public static Object toItemDescriptor(LocationResolver resolver) { + // TODO Get javadoc of LocationResolver? Could use docklet? But that would give dependency here + // on com.sun.javadoc.* + return resolver.getPrefix(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/test/java/brooklyn/cli/CliTest.java ---------------------------------------------------------------------- diff --git a/usage/cli/src/test/java/brooklyn/cli/CliTest.java b/usage/cli/src/test/java/brooklyn/cli/CliTest.java deleted file mode 100644 index 9bf173c..0000000 --- a/usage/cli/src/test/java/brooklyn/cli/CliTest.java +++ /dev/null @@ -1,604 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package brooklyn.cli; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; -import groovy.lang.GroovyClassLoader; -import io.airlift.command.Cli; -import io.airlift.command.Command; -import io.airlift.command.ParseException; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.InputStream; -import java.io.PrintStream; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; -import java.util.regex.Pattern; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.Assert; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import brooklyn.cli.AbstractMain.BrooklynCommand; -import brooklyn.cli.AbstractMain.BrooklynCommandCollectingArgs; -import brooklyn.cli.AbstractMain.HelpCommand; -import brooklyn.cli.Main.AppShutdownHandler; -import brooklyn.cli.Main.GeneratePasswordCommand; -import brooklyn.cli.Main.LaunchCommand; -import brooklyn.entity.Entity; -import brooklyn.entity.basic.AbstractApplication; -import brooklyn.entity.basic.AbstractEntity; -import brooklyn.entity.basic.ApplicationBuilder; -import brooklyn.entity.basic.Entities; -import brooklyn.entity.basic.StartableApplication; -import brooklyn.entity.proxying.EntityProxy; -import brooklyn.entity.proxying.ImplementedBy; -import brooklyn.entity.trait.Startable; -import brooklyn.location.Location; -import brooklyn.location.basic.SimulatedLocation; -import brooklyn.test.Asserts; -import brooklyn.test.entity.LocalManagementContextForTests; -import brooklyn.util.ResourceUtils; -import brooklyn.util.collections.MutableMap; -import brooklyn.util.exceptions.Exceptions; -import brooklyn.util.exceptions.FatalConfigurationRuntimeException; -import brooklyn.util.exceptions.UserFacingException; -import brooklyn.util.time.Duration; - -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; -import com.google.common.io.Files; - -public class CliTest { - - private static final Logger LOG = LoggerFactory.getLogger(CliTest.class); - - // See testInvokeGroovyScript test for usage - public static final AtomicBoolean GROOVY_INVOKED = new AtomicBoolean(false); - - private ExecutorService executor; - private StartableApplication app; - private static volatile ExampleEntity exampleEntity; - - // static so that they can be set from the static classes ExampleApp and ExampleEntity - private static volatile boolean exampleAppRunning; - private static volatile boolean exampleAppConstructed; - private static volatile boolean exampleEntityRunning; - - @BeforeMethod(alwaysRun=true) - public void setUp() throws Exception { - executor = Executors.newCachedThreadPool(); - exampleAppConstructed = false; - exampleAppRunning = false; - exampleEntityRunning = false; - } - - @AfterMethod(alwaysRun=true) - public void tearDown() throws Exception { - if (executor != null) executor.shutdownNow(); - if (app != null) Entities.destroyAll(app.getManagementContext()); - if (exampleEntity != null && exampleEntity.getApplication() != null) Entities.destroyAll(exampleEntity.getApplication().getManagementContext()); - } - - @Test - public void testLoadApplicationFromClasspath() throws Exception { - String appName = ExampleApp.class.getName(); - Object appBuilder = loadApplicationFromClasspathOrParse(appName); - assertTrue(appBuilder instanceof ApplicationBuilder, "app="+appBuilder); - assertAppWrappedInBuilder((ApplicationBuilder)appBuilder, ExampleApp.class.getCanonicalName()); - } - - @Test - public void testLoadApplicationBuilderFromClasspath() throws Exception { - String appName = ExampleAppBuilder.class.getName(); - Object appBuilder = loadApplicationFromClasspathOrParse(appName); - assertTrue(appBuilder instanceof ExampleAppBuilder, "app="+appBuilder); - } - - @Test - public void testLoadEntityFromClasspath() throws Exception { - String entityName = ExampleEntity.class.getName(); - Object appBuilder = loadApplicationFromClasspathOrParse(entityName); - assertTrue(appBuilder instanceof ApplicationBuilder, "app="+appBuilder); - - app = ((ApplicationBuilder)appBuilder).manage(); - Collection<Entity> entities = app.getChildren(); - assertEquals(entities.size(), 1, "entities="+entities); - assertTrue(Iterables.getOnlyElement(entities) instanceof ExampleEntity, "entities="+entities+"; ifs="+Iterables.getOnlyElement(entities).getClass().getInterfaces()); - assertTrue(Iterables.getOnlyElement(entities) instanceof EntityProxy, "entities="+entities); - } - - @Deprecated // Tests deprecated approach of using impl directly - @Test - public void testLoadEntityImplFromClasspath() throws Exception { - String entityName = ExampleEntityImpl.class.getName(); - Object appBuilder = loadApplicationFromClasspathOrParse(entityName); - assertTrue(appBuilder instanceof ApplicationBuilder, "app="+appBuilder); - - app = ((ApplicationBuilder)appBuilder).manage(); - Collection<Entity> entities = app.getChildren(); - assertEquals(entities.size(), 1, "entities="+entities); - assertEquals(Iterables.getOnlyElement(entities).getEntityType().getName(), ExampleEntity.class.getCanonicalName(), "entities="+entities); - assertTrue(Iterables.getOnlyElement(entities) instanceof EntityProxy, "entities="+entities); - } - - @Test - public void testLoadApplicationByParsingGroovyFile() throws Exception { - String appName = "ExampleAppInFile.groovy"; // file found in src/test/resources (contains empty app) - Object appBuilder = loadApplicationFromClasspathOrParse(appName); - assertTrue(appBuilder instanceof ApplicationBuilder, "app="+appBuilder); - assertAppWrappedInBuilder((ApplicationBuilder)appBuilder, "ExampleAppInFile"); - } - - private Object loadApplicationFromClasspathOrParse(String appName) throws Exception { - LaunchCommand launchCommand = new Main.LaunchCommand(); - ResourceUtils resourceUtils = ResourceUtils.create(this); - GroovyClassLoader loader = new GroovyClassLoader(CliTest.class.getClassLoader()); - return launchCommand.loadApplicationFromClasspathOrParse(resourceUtils, loader, appName); - } - - private void assertAppWrappedInBuilder(ApplicationBuilder builder, String expectedAppTypeName) { - StartableApplication app = builder.manage(); - try { - String typeName = app.getEntityType().getName(); - assertEquals(typeName, expectedAppTypeName, "app="+app+"; typeName="+typeName); - } finally { - Entities.destroyAll(app.getManagementContext()); - } - } - - @Test - public void testInvokeGroovyScript() throws Exception { - File groovyFile = File.createTempFile("testinvokegroovy", "groovy"); - try { - String contents = CliTest.class.getCanonicalName()+".GROOVY_INVOKED.set(true);"; - Files.write(contents.getBytes(), groovyFile); - - LaunchCommand launchCommand = new Main.LaunchCommand(); - ResourceUtils resourceUtils = ResourceUtils.create(this); - GroovyClassLoader loader = new GroovyClassLoader(CliTest.class.getClassLoader()); - launchCommand.execGroovyScript(resourceUtils, loader, groovyFile.toURI().toString()); - assertTrue(GROOVY_INVOKED.get()); - - } finally { - groovyFile.delete(); - GROOVY_INVOKED.set(false); - } - } - - @Test - public void testStopAllApplications() throws Exception { - LaunchCommand launchCommand = new Main.LaunchCommand(); - ExampleApp app = new ExampleApp(); - try { - Entities.startManagement(app); - app.start(ImmutableList.of(new SimulatedLocation())); - assertTrue(app.running); - - launchCommand.stopAllApps(ImmutableList.of(app)); - assertFalse(app.running); - } finally { - Entities.destroyAll(app.getManagementContext()); - } - } - - @Test - public void testWaitsForInterrupt() throws Exception { - final AppShutdownHandler listener = new AppShutdownHandler(); - Thread t = new Thread(new Runnable() { - @Override public void run() { - listener.waitOnShutdownRequest(); - }}); - - t.start(); - t.join(100); - assertTrue(t.isAlive()); - - t.interrupt(); - t.join(10*1000); - assertFalse(t.isAlive()); - } - - protected Cli<BrooklynCommand> buildCli() { - return new Main().cliBuilder().build(); - } - - @Test - public void testLaunchCommandParsesArgs() throws ParseException { - BrooklynCommand command = buildCli().parse("launch", - "--app", "my.App", - "--location", "localhost", - "--port", "1234", - "--bindAddress", "myhostname", - "--noConsole", "--noConsoleSecurity", "--stopOnKeyPress", - "--localBrooklynProperties", "/path/to/myprops", - LaunchCommand.PERSIST_OPTION, LaunchCommand.PERSIST_OPTION_REBIND, - "--persistenceDir", "/path/to/mypersist", - LaunchCommand.HA_OPTION, LaunchCommand.HA_OPTION_STANDBY); - assertTrue(command instanceof LaunchCommand, ""+command); - String details = command.toString(); - assertTrue(details.contains("app=my.App"), details); - assertTrue(details.contains("script=null"), details); - assertTrue(details.contains("location=localhost"), details); - assertTrue(details.contains("port=1234"), details); - assertTrue(details.contains("bindAddress=myhostname"), details); - assertTrue(details.contains("noConsole=true"), details); - assertTrue(details.contains("noConsoleSecurity=true"), details); - assertTrue(details.contains("stopOnKeyPress=true"), details); - assertTrue(details.contains("localBrooklynProperties=/path/to/myprops"), details); - assertTrue(details.contains("persist=rebind"), details); - assertTrue(details.contains("persistenceDir=/path/to/mypersist"), details); - assertTrue(details.contains("highAvailability=standby"), details); - } - - @Test - public void testLaunchCommandUsesDefaults() throws ParseException { - BrooklynCommand command = buildCli().parse("launch"); - assertTrue(command instanceof LaunchCommand, ""+command); - String details = command.toString(); - assertTrue(details.contains("app=null"), details); - assertTrue(details.contains("script=null"), details); - assertTrue(details.contains("location=null"), details); - assertTrue(details.contains("port=null"), details); - assertTrue(details.contains("noConsole=false"), details); - assertTrue(details.contains("noConsoleSecurity=false"), details); - assertTrue(details.contains("stopWhichAppsOnShutdown=theseIfNotPersisted"), details); - assertTrue(details.contains("stopOnKeyPress=false"), details); - assertTrue(details.contains("localBrooklynProperties=null"), details); - assertTrue(details.contains("persist=disabled"), details); - assertTrue(details.contains("persistenceDir=null"), details); - assertTrue(details.contains("highAvailability=auto"), details); - } - - @Test - public void testLaunchCommandComplainsWithInvalidArgs() { - Cli<BrooklynCommand> cli = buildCli(); - try { - BrooklynCommand command = cli.parse("launch", "invalid"); - command.call(); - Assert.fail("Should have thrown exception; instead got "+command); - } catch (ParseException e) { - /* expected */ - } catch (Exception e) { - throw Exceptions.propagate(e); - } - } - - @Test - public void testAppOptionIsOptional() throws ParseException { - Cli<BrooklynCommand> cli = buildCli(); - cli.parse("launch", "blah", "my.App"); - } - - public void testHelpCommand() { - Cli<BrooklynCommand> cli = buildCli(); - BrooklynCommand command = cli.parse("help"); - assertTrue(command instanceof HelpCommand); - command = cli.parse(); - assertTrue(command instanceof HelpCommand); - } - - @Test - public void testLaunchWillStartAppWhenGivenImpl() throws Exception { - Cli<BrooklynCommand> cli = buildCli(); - BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", ExampleApp.class.getName(), "--location", "localhost"); - submitCommandAndAssertRunnableSucceeds(command, new Runnable() { - public void run() { - assertTrue(exampleAppConstructed); - assertTrue(exampleAppRunning); - } - }); - } - - @Test - public void testLaunchStartsYamlApp() throws Exception { - Cli<BrooklynCommand> cli = buildCli(); - BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", "example-app-no-location.yaml", "--location", "localhost"); - submitCommandAndAssertRunnableSucceeds(command, new Runnable() { - public void run() { - assertTrue(exampleEntityRunning); - } - }); - } - - @Test - public void testLaunchStartsYamlAppWithCommandLineLocation() throws Exception { - Cli<BrooklynCommand> cli = buildCli(); - BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", "example-app-no-location.yaml", "--location", "localhost:(name=testLocalhost)"); - submitCommandAndAssertRunnableSucceeds(command, new Runnable() { - public void run() { - assertTrue(exampleEntityRunning); - assertTrue(Iterables.getOnlyElement(exampleEntity.getApplication().getLocations()).getDisplayName().equals("testLocalhost")); - } - }); - } - - @Test - public void testLaunchStartsYamlAppWithYamlAppLocation() throws Exception { - Cli<BrooklynCommand> cli = buildCli(); - BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", "example-app-app-location.yaml"); - submitCommandAndAssertRunnableSucceeds(command, new Runnable() { - public void run() { - assertTrue(exampleEntityRunning); - assertTrue(Iterables.getOnlyElement(exampleEntity.getApplication().getLocations()).getDisplayName().equals("appLocalhost")); - } - }); - } - - @Test - public void testLaunchStartsYamlAppWithYamlAndAppCliLocation() throws Exception { - Cli<BrooklynCommand> cli = buildCli(); - BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", "example-app-app-location.yaml", "--location", "localhost"); - submitCommandAndAssertRunnableSucceeds(command, new Runnable() { - public void run() { - assertTrue(exampleEntityRunning); - assertTrue(Iterables.getFirst(exampleEntity.getApplication().getLocations(), null).getDisplayName().equals("appLocalhost")); - } - }); - } - - @Test - public void testGeneratePasswordCommandParsed() throws Exception { - Cli<BrooklynCommand> cli = buildCli(); - BrooklynCommand command = cli.parse("generate-password", "--user", "myname"); - - assertTrue(command instanceof GeneratePasswordCommand); - } - - @Test - public void testGeneratePasswordFromStdin() throws Exception { - List<String> stdoutLines = runCommand(ImmutableList.of("generate-password", "--user", "myname", "--stdin"), "mypassword\nmypassword\n"); - - System.out.println(stdoutLines); - } - - @Test - public void testGeneratePasswordFailsIfPasswordsDontMatch() throws Throwable { - Throwable exception = runCommandExpectingException(ImmutableList.of("generate-password", "--user", "myname", "--stdin"), "mypassword\ndifferentpassword\n"); - if (exception instanceof UserFacingException && exception.toString().contains("Passwords did not match")) { - // success - } else { - throw new Exception(exception); - } - } - - @Test - public void testGeneratePasswordFailsIfNoConsole() throws Throwable { - Throwable exception = runCommandExpectingException(ImmutableList.of("generate-password", "--user", "myname"), ""); - if (exception instanceof FatalConfigurationRuntimeException && exception.toString().contains("No console")) { - // success - } else { - throw new Exception(exception); - } - } - - @Test - public void testGeneratePasswordFailsIfPasswordBlank() throws Throwable { - Throwable exception = runCommandExpectingException(ImmutableList.of("generate-password", "--user", "myname", "--stdin"), "\n\n"); - if (exception instanceof UserFacingException && exception.toString().contains("Password must not be blank")) { - // success - } else { - throw new Exception(exception); - } - } - - @Test - public void testInfoShowsDefaultBanner() throws Exception { - List<String> stdoutLines = runCommand(ImmutableList.of("info"), ""); - - for (String line : Splitter.on("\n").split(Main.DEFAULT_BANNER)) { - assertTrue(stdoutLines.contains(line), "out="+stdoutLines); - } - } - - @Test - public void testInfoSupportsCustomizedBanner() throws Exception { - String origBanner = Main.banner; - String origBannerFirstLine = Iterables.get(Splitter.on("\n").split(Main.DEFAULT_BANNER), 0); - try { - String customBanner = "My Custom Banner"; - Main.banner = customBanner; - List<String> stdoutLines = runCommand(ImmutableList.of("info"), ""); - - assertTrue(stdoutLines.contains(customBanner), "out="+stdoutLines); - assertFalse(stdoutLines.contains(origBannerFirstLine), "out="+stdoutLines); - } finally { - Main.banner = origBanner; - } - } - - @Test - public void testCanCustomiseInfoCommand() throws Exception { - Main main = new Main() { - protected Class<? extends BrooklynCommand> cliInfoCommand() { - return CustomInfoCommand.class; - } - }; - List<String> stdoutLines = runCommand(main.cliBuilder().build(), ImmutableList.of("info"), ""); - assertTrue(stdoutLines.contains("My Custom Info"), "out="+stdoutLines); - } - - @Command(name = "info", description = "Display information about brooklyn") - public static class CustomInfoCommand extends BrooklynCommandCollectingArgs { - @Override - public Void call() throws Exception { - System.out.println("My Custom Info"); - return null; - } - } - - @Test - public void testCanCustomiseLaunchCommand() throws Exception { - Main main = new Main() { - protected Class<? extends BrooklynCommand> cliLaunchCommand() { - return CustomLaunchCommand.class; - } - }; - List<String> stdoutLines = runCommand(main.cliBuilder().build(), ImmutableList.of("launch"), ""); - assertTrue(stdoutLines.contains("My Custom Launch"), "out="+stdoutLines); - } - - @Command(name = "launch", description = "Starts a server, optionally with applications") - public static class CustomLaunchCommand extends BrooklynCommandCollectingArgs { - @Override - public Void call() throws Exception { - System.out.println("My Custom Launch"); - return null; - } - } - - protected Throwable runCommandExpectingException(Iterable<String> args, String input) throws Exception { - try { - List<String> stdout = runCommand(args, input); - fail("Expected exception, but got stdout="+stdout); - return null; - } catch (ExecutionException e) { - return e.getCause(); - } - } - - protected List<String> runCommand(Iterable<String> args, String input) throws Exception { - Cli<BrooklynCommand> cli = buildCli(); - return runCommand(cli, args, input); - } - - protected List<String> runCommand(Cli<BrooklynCommand> cli, Iterable<String> args, String input) throws Exception { - final BrooklynCommand command = cli.parse(args); - - final AtomicReference<Exception> exception = new AtomicReference<Exception>(); - Thread t= new Thread(new Runnable() { - public void run() { - try { - command.call(); - } catch (Exception e) { - exception.set(e); - throw Exceptions.propagate(e); - } - }}); - - InputStream origIn = System.in; - PrintStream origOut = System.out; - try { - InputStream stdin = new ByteArrayInputStream(input.getBytes()); - System.setIn(stdin); - - ByteArrayOutputStream stdoutBytes = new ByteArrayOutputStream(); - PrintStream stdout = new PrintStream(stdoutBytes); - System.setOut(stdout); - - t.start(); - - t.join(10*1000); - assertFalse(t.isAlive()); - - if (exception.get() != null) { - throw new ExecutionException(exception.get()); - } - - return ImmutableList.copyOf(Splitter.on(Pattern.compile("\r?\n")).split(new String(stdoutBytes.toByteArray()))); - } finally { - System.setIn(origIn); - System.setOut(origOut); - t.interrupt(); - } - } - - private void submitCommandAndAssertRunnableSucceeds(final BrooklynCommand command, Runnable runnable) { - if (command instanceof LaunchCommand) { - ((LaunchCommand)command).useManagementContext(new LocalManagementContextForTests()); - } - executor.submit(new Callable<Void>() { - public Void call() throws Exception { - try { - LOG.info("Calling command: "+command); - command.call(); - return null; - } catch (Throwable t) { - LOG.error("Error executing command: "+t, t); - throw Exceptions.propagate(t); - } - }}); - - Asserts.succeedsEventually(MutableMap.of("timeout", Duration.ONE_MINUTE), runnable); - } - - // An empty app to be used for testing - public static class ExampleApp extends AbstractApplication { - volatile boolean running; - volatile boolean constructed; - - @Override public void init() { - super.init(); - constructed = true; - exampleAppConstructed = true; - } - @Override public void start(Collection<? extends Location> locations) { - super.start(locations); - running = true; - exampleAppRunning = true; - } - @Override public void stop() { - super.stop(); - running = false; - exampleAppRunning = false; - } - } - - // An empty entity to be used for testing - @ImplementedBy(ExampleEntityImpl.class) - public static interface ExampleEntity extends Entity, Startable { - } - - public static class ExampleEntityImpl extends AbstractEntity implements ExampleEntity { - public ExampleEntityImpl() { - super(); - exampleEntity = this; - } - @Override public void start(Collection<? extends Location> locations) { - exampleEntityRunning = true; - } - @Override public void stop() { - exampleEntityRunning = false; - } - @Override public void restart() { - } - } - - // An empty app builder to be used for testing - public static class ExampleAppBuilder extends ApplicationBuilder { - @Override protected void doBuild() { - // no-op - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/test/java/brooklyn/cli/CloudExplorerLiveTest.java ---------------------------------------------------------------------- diff --git a/usage/cli/src/test/java/brooklyn/cli/CloudExplorerLiveTest.java b/usage/cli/src/test/java/brooklyn/cli/CloudExplorerLiveTest.java deleted file mode 100644 index 6069f74..0000000 --- a/usage/cli/src/test/java/brooklyn/cli/CloudExplorerLiveTest.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package brooklyn.cli; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import io.airlift.command.Cli; -import io.airlift.command.ParseException; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.io.PrintStream; -import java.util.List; - -import org.testng.Assert; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.Test; - -import brooklyn.cli.AbstractMain.BrooklynCommand; - -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; - -public class CloudExplorerLiveTest { - - private String stdout; - private String stderr; - - @AfterMethod(alwaysRun=true) - public void tearDown() { - stdout = null; - stderr = null; - } - - @Test - public void testNoArgsThrows() throws Exception { - try { - call(new String[0]); - Assert.fail("No args should fail"); - } catch (ParseException e) { - Assert.assertTrue(e.toString().contains("No command specified"), ""+e); - } - } - - // A user running these tests might not have any instances; so don't assert that there will be one - @Test(groups={"Live", "Live-sanity"}) - public void testListInstances() throws Exception { - call("cloud-compute", "list-instances", "--location", "jclouds:aws-ec2:eu-west-1"); - - String errmsg = "stdout="+stdout+"; stderr="+stderr; - - List<String> lines = assertAndStipSingleLocationHeader(stdout); - assertTrue(lines.get(0).equals("Instances {"), errmsg); - assertTrue(lines.get(lines.size()-1).equals("}"), errmsg); - assertTrue(stderr.isEmpty(), errmsg); - } - - @Test(groups={"Live", "Live-sanity"}) - public void testListImages() throws Exception { - call("cloud-compute", "list-images", "--location", "jclouds:softlayer:ams01"); - - String errmsg = "stdout="+stdout+"; stderr="+stderr; - - // FIXME Now has location details pre-amble; fix assertions - List<String> lines = assertAndStipSingleLocationHeader(stdout); - assertTrue(lines.get(0).equals("Images {"), errmsg); - assertTrue(lines.get(lines.size()-1).equals("}"), errmsg); - assertTrue(stderr.isEmpty(), errmsg); - - List<String> imageLines = lines.subList(1, lines.size()-1); - assertTrue(imageLines.size() > 0, errmsg); - assertTrue(imageLines.get(0).matches(".*id=.*providerId=.*os=.*description=.*"), "line="+imageLines.get(0)+"; "+errmsg); - } - - @Test(groups={"Live", "Live-sanity"}) - public void testListHardwareProfiles() throws Exception { - call("cloud-compute", "list-hardware-profiles", "--location", "jclouds:softlayer:ams01"); - - String errmsg = "stdout="+stdout+"; stderr="+stderr; - - // FIXME Now has location details pre-amble; fix assertions - List<String> lines = assertAndStipSingleLocationHeader(stdout); - assertTrue(lines.get(0).equals("Hardware Profiles {"), errmsg); - assertTrue(lines.get(lines.size()-1).equals("}"), errmsg); - assertTrue(stderr.isEmpty(), errmsg); - - List<String> hardwareProfileLines = lines.subList(1, lines.size()-1); - assertTrue(hardwareProfileLines.size() > 0, errmsg); - assertTrue(hardwareProfileLines.get(0).matches(".*cpu=.*memory=.*processors=.*"), "line="+hardwareProfileLines.get(0)+"; "+errmsg); - } - - @Test(groups={"Live", "Live-sanity"}) - public void testGetImage() throws Exception { - call("cloud-compute", "get-image", "--location", "jclouds:softlayer:ams01", "CENTOS_6_64"); - - String errmsg = "stdout="+stdout+"; stderr="+stderr; - - // FIXME Now has location details pre-amble; fix assertions - List<String> lines = assertAndStipSingleLocationHeader(stdout); - assertTrue(lines.get(0).equals("Image CENTOS_6_64 {"), errmsg); - assertTrue(lines.get(lines.size()-1).equals("}"), errmsg); - assertTrue(stderr.isEmpty(), errmsg); - - List<String> imageLines = lines.subList(1, lines.size()-1); - assertTrue(imageLines.size() > 0, errmsg); - assertTrue(imageLines.get(0).matches(".*id=.*providerId=.*os=.*description=.*"), "line="+imageLines.get(0)+"; "+errmsg); - } - - @Test(groups={"Live", "Live-sanity"}) - public void testGetDefaultTemplate() throws Exception { - call("cloud-compute", "default-template", "--location", "jclouds:softlayer:ams01"); - - String errmsg = "stdout="+stdout+"; stderr="+stderr; - - // FIXME Now has location details pre-amble; fix assertions - List<String> lines = assertAndStipSingleLocationHeader(stdout); - assertTrue(lines.get(0).equals("Default template {"), errmsg); - assertTrue(lines.get(lines.size()-1).equals("}"), errmsg); - assertTrue(stderr.isEmpty(), errmsg); - - List<String> imageLines = lines.subList(1, lines.size()-1); - assertTrue(imageLines.size() > 0, errmsg); - assertTrue(imageLines.get(0).matches("\tImage.*id=.*providerId=.*os=.*description=.*"), "line="+imageLines.get(0)+"; "+errmsg); - assertTrue(imageLines.get(1).matches("\tHardware.*cpu=.*memory=.*processors=.*"), "line="+imageLines.get(1)+"; "+errmsg); - assertTrue(imageLines.get(2).matches("\tLocation.*scope=.*"), "line="+imageLines.get(2)+"; "+errmsg); - assertTrue(imageLines.get(3).matches("\tOptions.*"), "line="+imageLines.get(3)+"; "+errmsg); - } - - /** - * Expects in brooklyn.properties: - * brooklyn.location.named.softlayer-swift-ams01=jclouds:swift:https://ams01.objectstorage.softlayer.net/auth/v1.0 - * brooklyn.location.named.softlayer-swift-ams01.identity=ABCDEFGH:myusername - * brooklyn.location.named.softlayer-swift-ams01.credential=1234567890... - */ - @Test(groups={"Live", "Live-sanity"}) - public void testListContainers() throws Exception { - call("cloud-blobstore", "list-containers", "--location", "named:softlayer-swift-ams01"); - - String errmsg = "stdout="+stdout+"; stderr="+stderr; - - // FIXME Now has location details pre-amble; fix assertions - List<String> lines = assertAndStipSingleLocationHeader(stdout); - assertTrue(lines.get(0).equals("Containers {"), errmsg); - assertTrue(lines.get(lines.size()-1).equals("}"), errmsg); - assertTrue(stderr.isEmpty(), errmsg); - } - - protected void call(String... args) throws Exception { - call(new ByteArrayInputStream(new byte[0]), args); - } - - protected void call(InputStream instream, String... args) throws Exception { - ByteArrayOutputStream stdoutStream = new ByteArrayOutputStream(); - ByteArrayOutputStream stderrStream = new ByteArrayOutputStream(); - - Cli<BrooklynCommand> parser = new Main().cliBuilder().build(); - - BrooklynCommand command = parser.parse(args); - command.stdout = new PrintStream(stdoutStream); - command.stderr = new PrintStream(stderrStream); - command.stdin = instream; - try { - command.call(); - } finally { - stdout = new String(stdoutStream.toByteArray()); - stderr = new String(stderrStream.toByteArray()); - } - } - - private List<String> assertAndStipSingleLocationHeader(String stdout) { - List<String> lines = ImmutableList.copyOf(Splitter.on("\n").omitEmptyStrings().split(stdout)); - - String errmsg = "lines="+lines; - - int nextLineCount = 0; - assertEquals(lines.get(nextLineCount++), "Location {", errmsg); - assertEquals(lines.get(lines.size()-1), "}", errmsg); - assertTrue(lines.get(nextLineCount++).startsWith("\tprovider: "), errmsg); - assertTrue(lines.get(nextLineCount++).startsWith("\tdisplayName: "), errmsg); - assertTrue(lines.get(nextLineCount++).startsWith("\tidentity: "), errmsg); - if (lines.get(nextLineCount).startsWith("\tendpoint: ")) nextLineCount++; - if (lines.get(nextLineCount).startsWith("\tregion: ")) nextLineCount++; - - List<String> result = Lists.newArrayList(); - for (String line : lines.subList(nextLineCount, lines.size()-1)) { - assertTrue(line.startsWith("\t"), errmsg); - result.add(line.substring(1)); - } - return result; - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/test/java/org/apache/brooklyn/cli/CliTest.java ---------------------------------------------------------------------- diff --git a/usage/cli/src/test/java/org/apache/brooklyn/cli/CliTest.java b/usage/cli/src/test/java/org/apache/brooklyn/cli/CliTest.java new file mode 100644 index 0000000..96e3e93 --- /dev/null +++ b/usage/cli/src/test/java/org/apache/brooklyn/cli/CliTest.java @@ -0,0 +1,605 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.cli; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; +import groovy.lang.GroovyClassLoader; +import io.airlift.command.Cli; +import io.airlift.command.Command; +import io.airlift.command.ParseException; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.InputStream; +import java.io.PrintStream; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Pattern; + +import org.apache.brooklyn.cli.Main; +import org.apache.brooklyn.cli.AbstractMain.BrooklynCommand; +import org.apache.brooklyn.cli.AbstractMain.BrooklynCommandCollectingArgs; +import org.apache.brooklyn.cli.AbstractMain.HelpCommand; +import org.apache.brooklyn.cli.Main.AppShutdownHandler; +import org.apache.brooklyn.cli.Main.GeneratePasswordCommand; +import org.apache.brooklyn.cli.Main.LaunchCommand; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.entity.Entity; +import brooklyn.entity.basic.AbstractApplication; +import brooklyn.entity.basic.AbstractEntity; +import brooklyn.entity.basic.ApplicationBuilder; +import brooklyn.entity.basic.Entities; +import brooklyn.entity.basic.StartableApplication; +import brooklyn.entity.proxying.EntityProxy; +import brooklyn.entity.proxying.ImplementedBy; +import brooklyn.entity.trait.Startable; +import brooklyn.location.Location; +import brooklyn.location.basic.SimulatedLocation; +import brooklyn.test.Asserts; +import brooklyn.test.entity.LocalManagementContextForTests; +import brooklyn.util.ResourceUtils; +import brooklyn.util.collections.MutableMap; +import brooklyn.util.exceptions.Exceptions; +import brooklyn.util.exceptions.FatalConfigurationRuntimeException; +import brooklyn.util.exceptions.UserFacingException; +import brooklyn.util.time.Duration; + +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.io.Files; + +public class CliTest { + + private static final Logger LOG = LoggerFactory.getLogger(CliTest.class); + + // See testInvokeGroovyScript test for usage + public static final AtomicBoolean GROOVY_INVOKED = new AtomicBoolean(false); + + private ExecutorService executor; + private StartableApplication app; + private static volatile ExampleEntity exampleEntity; + + // static so that they can be set from the static classes ExampleApp and ExampleEntity + private static volatile boolean exampleAppRunning; + private static volatile boolean exampleAppConstructed; + private static volatile boolean exampleEntityRunning; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + executor = Executors.newCachedThreadPool(); + exampleAppConstructed = false; + exampleAppRunning = false; + exampleEntityRunning = false; + } + + @AfterMethod(alwaysRun=true) + public void tearDown() throws Exception { + if (executor != null) executor.shutdownNow(); + if (app != null) Entities.destroyAll(app.getManagementContext()); + if (exampleEntity != null && exampleEntity.getApplication() != null) Entities.destroyAll(exampleEntity.getApplication().getManagementContext()); + } + + @Test + public void testLoadApplicationFromClasspath() throws Exception { + String appName = ExampleApp.class.getName(); + Object appBuilder = loadApplicationFromClasspathOrParse(appName); + assertTrue(appBuilder instanceof ApplicationBuilder, "app="+appBuilder); + assertAppWrappedInBuilder((ApplicationBuilder)appBuilder, ExampleApp.class.getCanonicalName()); + } + + @Test + public void testLoadApplicationBuilderFromClasspath() throws Exception { + String appName = ExampleAppBuilder.class.getName(); + Object appBuilder = loadApplicationFromClasspathOrParse(appName); + assertTrue(appBuilder instanceof ExampleAppBuilder, "app="+appBuilder); + } + + @Test + public void testLoadEntityFromClasspath() throws Exception { + String entityName = ExampleEntity.class.getName(); + Object appBuilder = loadApplicationFromClasspathOrParse(entityName); + assertTrue(appBuilder instanceof ApplicationBuilder, "app="+appBuilder); + + app = ((ApplicationBuilder)appBuilder).manage(); + Collection<Entity> entities = app.getChildren(); + assertEquals(entities.size(), 1, "entities="+entities); + assertTrue(Iterables.getOnlyElement(entities) instanceof ExampleEntity, "entities="+entities+"; ifs="+Iterables.getOnlyElement(entities).getClass().getInterfaces()); + assertTrue(Iterables.getOnlyElement(entities) instanceof EntityProxy, "entities="+entities); + } + + @Deprecated // Tests deprecated approach of using impl directly + @Test + public void testLoadEntityImplFromClasspath() throws Exception { + String entityName = ExampleEntityImpl.class.getName(); + Object appBuilder = loadApplicationFromClasspathOrParse(entityName); + assertTrue(appBuilder instanceof ApplicationBuilder, "app="+appBuilder); + + app = ((ApplicationBuilder)appBuilder).manage(); + Collection<Entity> entities = app.getChildren(); + assertEquals(entities.size(), 1, "entities="+entities); + assertEquals(Iterables.getOnlyElement(entities).getEntityType().getName(), ExampleEntity.class.getCanonicalName(), "entities="+entities); + assertTrue(Iterables.getOnlyElement(entities) instanceof EntityProxy, "entities="+entities); + } + + @Test + public void testLoadApplicationByParsingGroovyFile() throws Exception { + String appName = "ExampleAppInFile.groovy"; // file found in src/test/resources (contains empty app) + Object appBuilder = loadApplicationFromClasspathOrParse(appName); + assertTrue(appBuilder instanceof ApplicationBuilder, "app="+appBuilder); + assertAppWrappedInBuilder((ApplicationBuilder)appBuilder, "ExampleAppInFile"); + } + + private Object loadApplicationFromClasspathOrParse(String appName) throws Exception { + LaunchCommand launchCommand = new Main.LaunchCommand(); + ResourceUtils resourceUtils = ResourceUtils.create(this); + GroovyClassLoader loader = new GroovyClassLoader(CliTest.class.getClassLoader()); + return launchCommand.loadApplicationFromClasspathOrParse(resourceUtils, loader, appName); + } + + private void assertAppWrappedInBuilder(ApplicationBuilder builder, String expectedAppTypeName) { + StartableApplication app = builder.manage(); + try { + String typeName = app.getEntityType().getName(); + assertEquals(typeName, expectedAppTypeName, "app="+app+"; typeName="+typeName); + } finally { + Entities.destroyAll(app.getManagementContext()); + } + } + + @Test + public void testInvokeGroovyScript() throws Exception { + File groovyFile = File.createTempFile("testinvokegroovy", "groovy"); + try { + String contents = CliTest.class.getCanonicalName()+".GROOVY_INVOKED.set(true);"; + Files.write(contents.getBytes(), groovyFile); + + LaunchCommand launchCommand = new Main.LaunchCommand(); + ResourceUtils resourceUtils = ResourceUtils.create(this); + GroovyClassLoader loader = new GroovyClassLoader(CliTest.class.getClassLoader()); + launchCommand.execGroovyScript(resourceUtils, loader, groovyFile.toURI().toString()); + assertTrue(GROOVY_INVOKED.get()); + + } finally { + groovyFile.delete(); + GROOVY_INVOKED.set(false); + } + } + + @Test + public void testStopAllApplications() throws Exception { + LaunchCommand launchCommand = new Main.LaunchCommand(); + ExampleApp app = new ExampleApp(); + try { + Entities.startManagement(app); + app.start(ImmutableList.of(new SimulatedLocation())); + assertTrue(app.running); + + launchCommand.stopAllApps(ImmutableList.of(app)); + assertFalse(app.running); + } finally { + Entities.destroyAll(app.getManagementContext()); + } + } + + @Test + public void testWaitsForInterrupt() throws Exception { + final AppShutdownHandler listener = new AppShutdownHandler(); + Thread t = new Thread(new Runnable() { + @Override public void run() { + listener.waitOnShutdownRequest(); + }}); + + t.start(); + t.join(100); + assertTrue(t.isAlive()); + + t.interrupt(); + t.join(10*1000); + assertFalse(t.isAlive()); + } + + protected Cli<BrooklynCommand> buildCli() { + return new Main().cliBuilder().build(); + } + + @Test + public void testLaunchCommandParsesArgs() throws ParseException { + BrooklynCommand command = buildCli().parse("launch", + "--app", "my.App", + "--location", "localhost", + "--port", "1234", + "--bindAddress", "myhostname", + "--noConsole", "--noConsoleSecurity", "--stopOnKeyPress", + "--localBrooklynProperties", "/path/to/myprops", + LaunchCommand.PERSIST_OPTION, LaunchCommand.PERSIST_OPTION_REBIND, + "--persistenceDir", "/path/to/mypersist", + LaunchCommand.HA_OPTION, LaunchCommand.HA_OPTION_STANDBY); + assertTrue(command instanceof LaunchCommand, ""+command); + String details = command.toString(); + assertTrue(details.contains("app=my.App"), details); + assertTrue(details.contains("script=null"), details); + assertTrue(details.contains("location=localhost"), details); + assertTrue(details.contains("port=1234"), details); + assertTrue(details.contains("bindAddress=myhostname"), details); + assertTrue(details.contains("noConsole=true"), details); + assertTrue(details.contains("noConsoleSecurity=true"), details); + assertTrue(details.contains("stopOnKeyPress=true"), details); + assertTrue(details.contains("localBrooklynProperties=/path/to/myprops"), details); + assertTrue(details.contains("persist=rebind"), details); + assertTrue(details.contains("persistenceDir=/path/to/mypersist"), details); + assertTrue(details.contains("highAvailability=standby"), details); + } + + @Test + public void testLaunchCommandUsesDefaults() throws ParseException { + BrooklynCommand command = buildCli().parse("launch"); + assertTrue(command instanceof LaunchCommand, ""+command); + String details = command.toString(); + assertTrue(details.contains("app=null"), details); + assertTrue(details.contains("script=null"), details); + assertTrue(details.contains("location=null"), details); + assertTrue(details.contains("port=null"), details); + assertTrue(details.contains("noConsole=false"), details); + assertTrue(details.contains("noConsoleSecurity=false"), details); + assertTrue(details.contains("stopWhichAppsOnShutdown=theseIfNotPersisted"), details); + assertTrue(details.contains("stopOnKeyPress=false"), details); + assertTrue(details.contains("localBrooklynProperties=null"), details); + assertTrue(details.contains("persist=disabled"), details); + assertTrue(details.contains("persistenceDir=null"), details); + assertTrue(details.contains("highAvailability=auto"), details); + } + + @Test + public void testLaunchCommandComplainsWithInvalidArgs() { + Cli<BrooklynCommand> cli = buildCli(); + try { + BrooklynCommand command = cli.parse("launch", "invalid"); + command.call(); + Assert.fail("Should have thrown exception; instead got "+command); + } catch (ParseException e) { + /* expected */ + } catch (Exception e) { + throw Exceptions.propagate(e); + } + } + + @Test + public void testAppOptionIsOptional() throws ParseException { + Cli<BrooklynCommand> cli = buildCli(); + cli.parse("launch", "blah", "my.App"); + } + + public void testHelpCommand() { + Cli<BrooklynCommand> cli = buildCli(); + BrooklynCommand command = cli.parse("help"); + assertTrue(command instanceof HelpCommand); + command = cli.parse(); + assertTrue(command instanceof HelpCommand); + } + + @Test + public void testLaunchWillStartAppWhenGivenImpl() throws Exception { + Cli<BrooklynCommand> cli = buildCli(); + BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", ExampleApp.class.getName(), "--location", "localhost"); + submitCommandAndAssertRunnableSucceeds(command, new Runnable() { + public void run() { + assertTrue(exampleAppConstructed); + assertTrue(exampleAppRunning); + } + }); + } + + @Test + public void testLaunchStartsYamlApp() throws Exception { + Cli<BrooklynCommand> cli = buildCli(); + BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", "example-app-no-location.yaml", "--location", "localhost"); + submitCommandAndAssertRunnableSucceeds(command, new Runnable() { + public void run() { + assertTrue(exampleEntityRunning); + } + }); + } + + @Test + public void testLaunchStartsYamlAppWithCommandLineLocation() throws Exception { + Cli<BrooklynCommand> cli = buildCli(); + BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", "example-app-no-location.yaml", "--location", "localhost:(name=testLocalhost)"); + submitCommandAndAssertRunnableSucceeds(command, new Runnable() { + public void run() { + assertTrue(exampleEntityRunning); + assertTrue(Iterables.getOnlyElement(exampleEntity.getApplication().getLocations()).getDisplayName().equals("testLocalhost")); + } + }); + } + + @Test + public void testLaunchStartsYamlAppWithYamlAppLocation() throws Exception { + Cli<BrooklynCommand> cli = buildCli(); + BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", "example-app-app-location.yaml"); + submitCommandAndAssertRunnableSucceeds(command, new Runnable() { + public void run() { + assertTrue(exampleEntityRunning); + assertTrue(Iterables.getOnlyElement(exampleEntity.getApplication().getLocations()).getDisplayName().equals("appLocalhost")); + } + }); + } + + @Test + public void testLaunchStartsYamlAppWithYamlAndAppCliLocation() throws Exception { + Cli<BrooklynCommand> cli = buildCli(); + BrooklynCommand command = cli.parse("launch", "--noConsole", "--app", "example-app-app-location.yaml", "--location", "localhost"); + submitCommandAndAssertRunnableSucceeds(command, new Runnable() { + public void run() { + assertTrue(exampleEntityRunning); + assertTrue(Iterables.getFirst(exampleEntity.getApplication().getLocations(), null).getDisplayName().equals("appLocalhost")); + } + }); + } + + @Test + public void testGeneratePasswordCommandParsed() throws Exception { + Cli<BrooklynCommand> cli = buildCli(); + BrooklynCommand command = cli.parse("generate-password", "--user", "myname"); + + assertTrue(command instanceof GeneratePasswordCommand); + } + + @Test + public void testGeneratePasswordFromStdin() throws Exception { + List<String> stdoutLines = runCommand(ImmutableList.of("generate-password", "--user", "myname", "--stdin"), "mypassword\nmypassword\n"); + + System.out.println(stdoutLines); + } + + @Test + public void testGeneratePasswordFailsIfPasswordsDontMatch() throws Throwable { + Throwable exception = runCommandExpectingException(ImmutableList.of("generate-password", "--user", "myname", "--stdin"), "mypassword\ndifferentpassword\n"); + if (exception instanceof UserFacingException && exception.toString().contains("Passwords did not match")) { + // success + } else { + throw new Exception(exception); + } + } + + @Test + public void testGeneratePasswordFailsIfNoConsole() throws Throwable { + Throwable exception = runCommandExpectingException(ImmutableList.of("generate-password", "--user", "myname"), ""); + if (exception instanceof FatalConfigurationRuntimeException && exception.toString().contains("No console")) { + // success + } else { + throw new Exception(exception); + } + } + + @Test + public void testGeneratePasswordFailsIfPasswordBlank() throws Throwable { + Throwable exception = runCommandExpectingException(ImmutableList.of("generate-password", "--user", "myname", "--stdin"), "\n\n"); + if (exception instanceof UserFacingException && exception.toString().contains("Password must not be blank")) { + // success + } else { + throw new Exception(exception); + } + } + + @Test + public void testInfoShowsDefaultBanner() throws Exception { + List<String> stdoutLines = runCommand(ImmutableList.of("info"), ""); + + for (String line : Splitter.on("\n").split(Main.DEFAULT_BANNER)) { + assertTrue(stdoutLines.contains(line), "out="+stdoutLines); + } + } + + @Test + public void testInfoSupportsCustomizedBanner() throws Exception { + String origBanner = Main.banner; + String origBannerFirstLine = Iterables.get(Splitter.on("\n").split(Main.DEFAULT_BANNER), 0); + try { + String customBanner = "My Custom Banner"; + Main.banner = customBanner; + List<String> stdoutLines = runCommand(ImmutableList.of("info"), ""); + + assertTrue(stdoutLines.contains(customBanner), "out="+stdoutLines); + assertFalse(stdoutLines.contains(origBannerFirstLine), "out="+stdoutLines); + } finally { + Main.banner = origBanner; + } + } + + @Test + public void testCanCustomiseInfoCommand() throws Exception { + Main main = new Main() { + protected Class<? extends BrooklynCommand> cliInfoCommand() { + return CustomInfoCommand.class; + } + }; + List<String> stdoutLines = runCommand(main.cliBuilder().build(), ImmutableList.of("info"), ""); + assertTrue(stdoutLines.contains("My Custom Info"), "out="+stdoutLines); + } + + @Command(name = "info", description = "Display information about brooklyn") + public static class CustomInfoCommand extends BrooklynCommandCollectingArgs { + @Override + public Void call() throws Exception { + System.out.println("My Custom Info"); + return null; + } + } + + @Test + public void testCanCustomiseLaunchCommand() throws Exception { + Main main = new Main() { + protected Class<? extends BrooklynCommand> cliLaunchCommand() { + return CustomLaunchCommand.class; + } + }; + List<String> stdoutLines = runCommand(main.cliBuilder().build(), ImmutableList.of("launch"), ""); + assertTrue(stdoutLines.contains("My Custom Launch"), "out="+stdoutLines); + } + + @Command(name = "launch", description = "Starts a server, optionally with applications") + public static class CustomLaunchCommand extends BrooklynCommandCollectingArgs { + @Override + public Void call() throws Exception { + System.out.println("My Custom Launch"); + return null; + } + } + + protected Throwable runCommandExpectingException(Iterable<String> args, String input) throws Exception { + try { + List<String> stdout = runCommand(args, input); + fail("Expected exception, but got stdout="+stdout); + return null; + } catch (ExecutionException e) { + return e.getCause(); + } + } + + protected List<String> runCommand(Iterable<String> args, String input) throws Exception { + Cli<BrooklynCommand> cli = buildCli(); + return runCommand(cli, args, input); + } + + protected List<String> runCommand(Cli<BrooklynCommand> cli, Iterable<String> args, String input) throws Exception { + final BrooklynCommand command = cli.parse(args); + + final AtomicReference<Exception> exception = new AtomicReference<Exception>(); + Thread t= new Thread(new Runnable() { + public void run() { + try { + command.call(); + } catch (Exception e) { + exception.set(e); + throw Exceptions.propagate(e); + } + }}); + + InputStream origIn = System.in; + PrintStream origOut = System.out; + try { + InputStream stdin = new ByteArrayInputStream(input.getBytes()); + System.setIn(stdin); + + ByteArrayOutputStream stdoutBytes = new ByteArrayOutputStream(); + PrintStream stdout = new PrintStream(stdoutBytes); + System.setOut(stdout); + + t.start(); + + t.join(10*1000); + assertFalse(t.isAlive()); + + if (exception.get() != null) { + throw new ExecutionException(exception.get()); + } + + return ImmutableList.copyOf(Splitter.on(Pattern.compile("\r?\n")).split(new String(stdoutBytes.toByteArray()))); + } finally { + System.setIn(origIn); + System.setOut(origOut); + t.interrupt(); + } + } + + private void submitCommandAndAssertRunnableSucceeds(final BrooklynCommand command, Runnable runnable) { + if (command instanceof LaunchCommand) { + ((LaunchCommand)command).useManagementContext(new LocalManagementContextForTests()); + } + executor.submit(new Callable<Void>() { + public Void call() throws Exception { + try { + LOG.info("Calling command: "+command); + command.call(); + return null; + } catch (Throwable t) { + LOG.error("Error executing command: "+t, t); + throw Exceptions.propagate(t); + } + }}); + + Asserts.succeedsEventually(MutableMap.of("timeout", Duration.ONE_MINUTE), runnable); + } + + // An empty app to be used for testing + public static class ExampleApp extends AbstractApplication { + volatile boolean running; + volatile boolean constructed; + + @Override public void init() { + super.init(); + constructed = true; + exampleAppConstructed = true; + } + @Override public void start(Collection<? extends Location> locations) { + super.start(locations); + running = true; + exampleAppRunning = true; + } + @Override public void stop() { + super.stop(); + running = false; + exampleAppRunning = false; + } + } + + // An empty entity to be used for testing + @ImplementedBy(ExampleEntityImpl.class) + public static interface ExampleEntity extends Entity, Startable { + } + + public static class ExampleEntityImpl extends AbstractEntity implements ExampleEntity { + public ExampleEntityImpl() { + super(); + exampleEntity = this; + } + @Override public void start(Collection<? extends Location> locations) { + exampleEntityRunning = true; + } + @Override public void stop() { + exampleEntityRunning = false; + } + @Override public void restart() { + } + } + + // An empty app builder to be used for testing + public static class ExampleAppBuilder extends ApplicationBuilder { + @Override protected void doBuild() { + // no-op + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/test/java/org/apache/brooklyn/cli/CloudExplorerLiveTest.java ---------------------------------------------------------------------- diff --git a/usage/cli/src/test/java/org/apache/brooklyn/cli/CloudExplorerLiveTest.java b/usage/cli/src/test/java/org/apache/brooklyn/cli/CloudExplorerLiveTest.java new file mode 100644 index 0000000..8abe1c0 --- /dev/null +++ b/usage/cli/src/test/java/org/apache/brooklyn/cli/CloudExplorerLiveTest.java @@ -0,0 +1,209 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.cli; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import io.airlift.command.Cli; +import io.airlift.command.ParseException; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; +import java.util.List; + +import org.apache.brooklyn.cli.Main; +import org.apache.brooklyn.cli.AbstractMain.BrooklynCommand; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.Test; + +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +public class CloudExplorerLiveTest { + + private String stdout; + private String stderr; + + @AfterMethod(alwaysRun=true) + public void tearDown() { + stdout = null; + stderr = null; + } + + @Test + public void testNoArgsThrows() throws Exception { + try { + call(new String[0]); + Assert.fail("No args should fail"); + } catch (ParseException e) { + Assert.assertTrue(e.toString().contains("No command specified"), ""+e); + } + } + + // A user running these tests might not have any instances; so don't assert that there will be one + @Test(groups={"Live", "Live-sanity"}) + public void testListInstances() throws Exception { + call("cloud-compute", "list-instances", "--location", "jclouds:aws-ec2:eu-west-1"); + + String errmsg = "stdout="+stdout+"; stderr="+stderr; + + List<String> lines = assertAndStipSingleLocationHeader(stdout); + assertTrue(lines.get(0).equals("Instances {"), errmsg); + assertTrue(lines.get(lines.size()-1).equals("}"), errmsg); + assertTrue(stderr.isEmpty(), errmsg); + } + + @Test(groups={"Live", "Live-sanity"}) + public void testListImages() throws Exception { + call("cloud-compute", "list-images", "--location", "jclouds:softlayer:ams01"); + + String errmsg = "stdout="+stdout+"; stderr="+stderr; + + // FIXME Now has location details pre-amble; fix assertions + List<String> lines = assertAndStipSingleLocationHeader(stdout); + assertTrue(lines.get(0).equals("Images {"), errmsg); + assertTrue(lines.get(lines.size()-1).equals("}"), errmsg); + assertTrue(stderr.isEmpty(), errmsg); + + List<String> imageLines = lines.subList(1, lines.size()-1); + assertTrue(imageLines.size() > 0, errmsg); + assertTrue(imageLines.get(0).matches(".*id=.*providerId=.*os=.*description=.*"), "line="+imageLines.get(0)+"; "+errmsg); + } + + @Test(groups={"Live", "Live-sanity"}) + public void testListHardwareProfiles() throws Exception { + call("cloud-compute", "list-hardware-profiles", "--location", "jclouds:softlayer:ams01"); + + String errmsg = "stdout="+stdout+"; stderr="+stderr; + + // FIXME Now has location details pre-amble; fix assertions + List<String> lines = assertAndStipSingleLocationHeader(stdout); + assertTrue(lines.get(0).equals("Hardware Profiles {"), errmsg); + assertTrue(lines.get(lines.size()-1).equals("}"), errmsg); + assertTrue(stderr.isEmpty(), errmsg); + + List<String> hardwareProfileLines = lines.subList(1, lines.size()-1); + assertTrue(hardwareProfileLines.size() > 0, errmsg); + assertTrue(hardwareProfileLines.get(0).matches(".*cpu=.*memory=.*processors=.*"), "line="+hardwareProfileLines.get(0)+"; "+errmsg); + } + + @Test(groups={"Live", "Live-sanity"}) + public void testGetImage() throws Exception { + call("cloud-compute", "get-image", "--location", "jclouds:softlayer:ams01", "CENTOS_6_64"); + + String errmsg = "stdout="+stdout+"; stderr="+stderr; + + // FIXME Now has location details pre-amble; fix assertions + List<String> lines = assertAndStipSingleLocationHeader(stdout); + assertTrue(lines.get(0).equals("Image CENTOS_6_64 {"), errmsg); + assertTrue(lines.get(lines.size()-1).equals("}"), errmsg); + assertTrue(stderr.isEmpty(), errmsg); + + List<String> imageLines = lines.subList(1, lines.size()-1); + assertTrue(imageLines.size() > 0, errmsg); + assertTrue(imageLines.get(0).matches(".*id=.*providerId=.*os=.*description=.*"), "line="+imageLines.get(0)+"; "+errmsg); + } + + @Test(groups={"Live", "Live-sanity"}) + public void testGetDefaultTemplate() throws Exception { + call("cloud-compute", "default-template", "--location", "jclouds:softlayer:ams01"); + + String errmsg = "stdout="+stdout+"; stderr="+stderr; + + // FIXME Now has location details pre-amble; fix assertions + List<String> lines = assertAndStipSingleLocationHeader(stdout); + assertTrue(lines.get(0).equals("Default template {"), errmsg); + assertTrue(lines.get(lines.size()-1).equals("}"), errmsg); + assertTrue(stderr.isEmpty(), errmsg); + + List<String> imageLines = lines.subList(1, lines.size()-1); + assertTrue(imageLines.size() > 0, errmsg); + assertTrue(imageLines.get(0).matches("\tImage.*id=.*providerId=.*os=.*description=.*"), "line="+imageLines.get(0)+"; "+errmsg); + assertTrue(imageLines.get(1).matches("\tHardware.*cpu=.*memory=.*processors=.*"), "line="+imageLines.get(1)+"; "+errmsg); + assertTrue(imageLines.get(2).matches("\tLocation.*scope=.*"), "line="+imageLines.get(2)+"; "+errmsg); + assertTrue(imageLines.get(3).matches("\tOptions.*"), "line="+imageLines.get(3)+"; "+errmsg); + } + + /** + * Expects in brooklyn.properties: + * brooklyn.location.named.softlayer-swift-ams01=jclouds:swift:https://ams01.objectstorage.softlayer.net/auth/v1.0 + * brooklyn.location.named.softlayer-swift-ams01.identity=ABCDEFGH:myusername + * brooklyn.location.named.softlayer-swift-ams01.credential=1234567890... + */ + @Test(groups={"Live", "Live-sanity"}) + public void testListContainers() throws Exception { + call("cloud-blobstore", "list-containers", "--location", "named:softlayer-swift-ams01"); + + String errmsg = "stdout="+stdout+"; stderr="+stderr; + + // FIXME Now has location details pre-amble; fix assertions + List<String> lines = assertAndStipSingleLocationHeader(stdout); + assertTrue(lines.get(0).equals("Containers {"), errmsg); + assertTrue(lines.get(lines.size()-1).equals("}"), errmsg); + assertTrue(stderr.isEmpty(), errmsg); + } + + protected void call(String... args) throws Exception { + call(new ByteArrayInputStream(new byte[0]), args); + } + + protected void call(InputStream instream, String... args) throws Exception { + ByteArrayOutputStream stdoutStream = new ByteArrayOutputStream(); + ByteArrayOutputStream stderrStream = new ByteArrayOutputStream(); + + Cli<BrooklynCommand> parser = new Main().cliBuilder().build(); + + BrooklynCommand command = parser.parse(args); + command.stdout = new PrintStream(stdoutStream); + command.stderr = new PrintStream(stderrStream); + command.stdin = instream; + try { + command.call(); + } finally { + stdout = new String(stdoutStream.toByteArray()); + stderr = new String(stderrStream.toByteArray()); + } + } + + private List<String> assertAndStipSingleLocationHeader(String stdout) { + List<String> lines = ImmutableList.copyOf(Splitter.on("\n").omitEmptyStrings().split(stdout)); + + String errmsg = "lines="+lines; + + int nextLineCount = 0; + assertEquals(lines.get(nextLineCount++), "Location {", errmsg); + assertEquals(lines.get(lines.size()-1), "}", errmsg); + assertTrue(lines.get(nextLineCount++).startsWith("\tprovider: "), errmsg); + assertTrue(lines.get(nextLineCount++).startsWith("\tdisplayName: "), errmsg); + assertTrue(lines.get(nextLineCount++).startsWith("\tidentity: "), errmsg); + if (lines.get(nextLineCount).startsWith("\tendpoint: ")) nextLineCount++; + if (lines.get(nextLineCount).startsWith("\tregion: ")) nextLineCount++; + + List<String> result = Lists.newArrayList(); + for (String line : lines.subList(nextLineCount, lines.size()-1)) { + assertTrue(line.startsWith("\t"), errmsg); + result.add(line.substring(1)); + } + return result; + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/test/resources/example-app-app-location.yaml ---------------------------------------------------------------------- diff --git a/usage/cli/src/test/resources/example-app-app-location.yaml b/usage/cli/src/test/resources/example-app-app-location.yaml index aca5362..f161e8d 100644 --- a/usage/cli/src/test/resources/example-app-app-location.yaml +++ b/usage/cli/src/test/resources/example-app-app-location.yaml @@ -19,5 +19,5 @@ name: example-app-app-location location: localhost:(name=appLocalhost) services: -- serviceType: brooklyn.cli.CliTest$ExampleEntity +- serviceType: org.apache.brooklyn.cli.CliTest$ExampleEntity name: Example app \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/test/resources/example-app-entity-location.yaml ---------------------------------------------------------------------- diff --git a/usage/cli/src/test/resources/example-app-entity-location.yaml b/usage/cli/src/test/resources/example-app-entity-location.yaml index cbca856..5b2e466 100644 --- a/usage/cli/src/test/resources/example-app-entity-location.yaml +++ b/usage/cli/src/test/resources/example-app-entity-location.yaml @@ -18,6 +18,6 @@ # name: example-app-entity-location services: -- serviceType: brooklyn.cli.CliTest$ExampleEntity +- serviceType: org.apache.brooklyn.cli.CliTest$ExampleEntity name: Example app location: localhost:(name=entityLocalhost) \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/74ee6aac/usage/cli/src/test/resources/example-app-no-location.yaml ---------------------------------------------------------------------- diff --git a/usage/cli/src/test/resources/example-app-no-location.yaml b/usage/cli/src/test/resources/example-app-no-location.yaml index 9592e61..60cff1c 100644 --- a/usage/cli/src/test/resources/example-app-no-location.yaml +++ b/usage/cli/src/test/resources/example-app-no-location.yaml @@ -18,5 +18,5 @@ # name: example-app-no-location services: -- serviceType: brooklyn.cli.CliTest$ExampleEntity +- serviceType: org.apache.brooklyn.cli.CliTest$ExampleEntity name: Example app
