http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/AbstractChefToyMySqlEntityLiveTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/AbstractChefToyMySqlEntityLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/AbstractChefToyMySqlEntityLiveTest.java new file mode 100644 index 0000000..3250ee3 --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/AbstractChefToyMySqlEntityLiveTest.java @@ -0,0 +1,40 @@ +/* + * 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.entity.chef.mysql; + +import org.testng.annotations.Test; +import org.apache.brooklyn.api.location.MachineProvisioningLocation; +import org.apache.brooklyn.entity.chef.ChefLiveTestSupport; +import org.apache.brooklyn.entity.software.base.test.mysql.AbstractToyMySqlEntityTest; +import org.apache.brooklyn.location.basic.SshMachineLocation; + +public abstract class AbstractChefToyMySqlEntityLiveTest extends AbstractToyMySqlEntityTest { + + @Override + // mark as live here + @Test(groups = "Live") + public void testMySqlOnProvisioningLocation() throws Exception { + super.testMySqlOnProvisioningLocation(); + } + + protected MachineProvisioningLocation<? extends SshMachineLocation> createLocation() { + return ChefLiveTestSupport.createLocation(mgmt); + } + +}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/ChefSoloDriverMySqlEntityLiveTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/ChefSoloDriverMySqlEntityLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/ChefSoloDriverMySqlEntityLiveTest.java new file mode 100644 index 0000000..e6c7b27 --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/ChefSoloDriverMySqlEntityLiveTest.java @@ -0,0 +1,49 @@ +/* + * 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.entity.chef.mysql; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.entity.core.Entities; +import org.apache.brooklyn.entity.software.base.SoftwareProcess; +import org.apache.brooklyn.sensor.ssh.SshEffectorTasks; +import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper; +import org.testng.annotations.Test; + +public class ChefSoloDriverMySqlEntityLiveTest extends AbstractChefToyMySqlEntityLiveTest { + + // test here just so Eclipse IDE picks it up + @Override @Test(groups="Live") + public void testMySqlOnProvisioningLocation() throws Exception { + super.testMySqlOnProvisioningLocation(); + } + + @Override + protected Integer getPid(Entity mysql) { + ProcessTaskWrapper<Integer> t = Entities.submit(mysql, SshEffectorTasks.ssh("sudo cat "+ChefSoloDriverToyMySqlEntity.PID_FILE)); + return Integer.parseInt(t.block().getStdout().trim()); + } + + @Override + protected Entity createMysql() { + return app.createAndManageChild(EntitySpec.create(Entity.class, ChefSoloDriverToyMySqlEntity.class). + additionalInterfaces(SoftwareProcess.class)); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/ChefSoloDriverToyMySqlEntity.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/ChefSoloDriverToyMySqlEntity.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/ChefSoloDriverToyMySqlEntity.java new file mode 100644 index 0000000..c36dc21 --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/ChefSoloDriverToyMySqlEntity.java @@ -0,0 +1,89 @@ +/* + * 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.entity.chef.mysql; + +import org.apache.brooklyn.api.mgmt.TaskAdaptable; +import org.apache.brooklyn.api.mgmt.TaskFactory; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.ConfigKeys; +import org.apache.brooklyn.entity.chef.ChefConfig; +import org.apache.brooklyn.entity.chef.ChefConfigs; +import org.apache.brooklyn.entity.chef.ChefSoloDriver; +import org.apache.brooklyn.entity.software.base.SoftwareProcessImpl; +import org.apache.brooklyn.sensor.feed.ssh.SshFeed; +import org.apache.brooklyn.sensor.feed.ssh.SshPollConfig; +import org.apache.brooklyn.sensor.ssh.SshEffectorTasks; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.time.Duration; + +@Deprecated /** @deprecated since 0.7.0 use see examples {Dynamic,Typed}ToyMySqlEntityChef */ +public class ChefSoloDriverToyMySqlEntity extends SoftwareProcessImpl implements ChefConfig { + + public static final String PID_FILE = "/var/run/mysqld/mysqld.pid"; + public static final ConfigKey<TaskFactory<? extends TaskAdaptable<Boolean>>> IS_RUNNING_TASK = + ConfigKeys.newConfigKeyWithDefault(ChefSoloDriver.IS_RUNNING_TASK, + SshEffectorTasks.isPidFromFileRunning(PID_FILE).runAsRoot()); + + public static final ConfigKey<TaskFactory<?>> STOP_TASK = + ConfigKeys.newConfigKeyWithDefault(ChefSoloDriver.STOP_TASK, + SshEffectorTasks.ssh("/etc/init.d/mysql stop").allowingNonZeroExitCode().runAsRoot()); + + private SshFeed upFeed; + + @Override + public Class<?> getDriverInterface() { + return ChefSoloDriver.class; + } + + @Override + protected void connectSensors() { + super.connectSensors(); + + // TODO have a TaskFactoryFeed which reuses the IS_RUNNING_TASK + upFeed = SshFeed.builder().entity(this).period(Duration.FIVE_SECONDS.toMilliseconds()) + .poll(new SshPollConfig<Boolean>(SERVICE_UP) + .command("ps -p `sudo cat /var/run/mysqld/mysqld.pid`") + .setOnSuccess(true).setOnFailureOrException(false)) + .build(); + } + + @Override + protected void disconnectSensors() { + // TODO nicer way to disconnect + if (upFeed != null) upFeed.stop(); + super.disconnectSensors(); + } + + @Override + public void init() { + super.init(); + ChefConfigs.addToLaunchRunList(this, "mysql::server"); + ChefConfigs.addToCookbooksFromGithub(this, "mysql", "build-essential", "openssl"); + ChefConfigs.setLaunchAttribute(this, "mysql", + MutableMap.of() + .add("server_root_password", "MyPassword") + .add("server_debian_password", "MyPassword") + .add("server_repl_password", "MyPassword") + ); + + // TODO other attributes, eg: + // node['mysql']['port'] + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefAutodetectToyMySqlEntityLiveTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefAutodetectToyMySqlEntityLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefAutodetectToyMySqlEntityLiveTest.java new file mode 100644 index 0000000..0102f6e --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefAutodetectToyMySqlEntityLiveTest.java @@ -0,0 +1,43 @@ +/* + * 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.entity.chef.mysql; + +import org.apache.brooklyn.api.entity.Entity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Test; + +public class DynamicChefAutodetectToyMySqlEntityLiveTest extends AbstractChefToyMySqlEntityLiveTest { + + private static final Logger log = LoggerFactory.getLogger(DynamicChefAutodetectToyMySqlEntityLiveTest.class); + + // test here just so Eclipse IDE picks it up + @Override @Test(groups="Live") + public void testMySqlOnProvisioningLocation() throws Exception { + super.testMySqlOnProvisioningLocation(); + } + + @Override + protected Entity createMysql() { + Entity mysql = app.createAndManageChild(DynamicToyMySqlEntityChef.spec()); + log.debug("created "+mysql); + return mysql; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefServerToyMySqlEntityLiveTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefServerToyMySqlEntityLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefServerToyMySqlEntityLiveTest.java new file mode 100644 index 0000000..566a96e --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefServerToyMySqlEntityLiveTest.java @@ -0,0 +1,50 @@ +/* + * 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.entity.chef.mysql; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.entity.chef.ChefLiveTestSupport; +import org.apache.brooklyn.entity.chef.ChefServerTasksIntegrationTest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Test; + +/** Expects knife on the path, but will use Brooklyn registered account, + * and that account has the mysql recipe installed. + * <p> + * See {@link ChefServerTasksIntegrationTest} for more info. */ +public class DynamicChefServerToyMySqlEntityLiveTest extends AbstractChefToyMySqlEntityLiveTest { + + private static final Logger log = LoggerFactory.getLogger(DynamicChefServerToyMySqlEntityLiveTest.class); + + // test here just so Eclipse IDE picks it up + @Override @Test(groups="Live") + public void testMySqlOnProvisioningLocation() throws Exception { + super.testMySqlOnProvisioningLocation(); + } + + @Override + protected Entity createMysql() { + ChefLiveTestSupport.installBrooklynChefHostedConfig(app); + Entity mysql = app.createAndManageChild(DynamicToyMySqlEntityChef.specKnife()); + log.debug("created "+mysql); + return mysql; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefSoloToyMySqlEntityLiveTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefSoloToyMySqlEntityLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefSoloToyMySqlEntityLiveTest.java new file mode 100644 index 0000000..ba6c6d9 --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefSoloToyMySqlEntityLiveTest.java @@ -0,0 +1,43 @@ +/* + * 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.entity.chef.mysql; + +import org.apache.brooklyn.api.entity.Entity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Test; + +public class DynamicChefSoloToyMySqlEntityLiveTest extends AbstractChefToyMySqlEntityLiveTest { + + private static final Logger log = LoggerFactory.getLogger(DynamicChefSoloToyMySqlEntityLiveTest.class); + + // test here just so Eclipse IDE picks it up + @Override @Test(groups="Live") + public void testMySqlOnProvisioningLocation() throws Exception { + super.testMySqlOnProvisioningLocation(); + } + + @Override + protected Entity createMysql() { + Entity mysql = app.createAndManageChild(DynamicToyMySqlEntityChef.specSolo()); + log.debug("created "+mysql); + return mysql; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicToyMySqlEntityChef.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicToyMySqlEntityChef.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicToyMySqlEntityChef.java new file mode 100644 index 0000000..2edd8a1 --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicToyMySqlEntityChef.java @@ -0,0 +1,81 @@ +/* + * 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.entity.chef.mysql; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.entity.chef.ChefConfig; +import org.apache.brooklyn.entity.chef.ChefConfigs; +import org.apache.brooklyn.entity.chef.ChefEntity; +import org.apache.brooklyn.util.collections.MutableMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** Builds up a MySql entity via chef using specs only */ +public class DynamicToyMySqlEntityChef implements ChefConfig { + + private static final Logger log = LoggerFactory.getLogger(DynamicToyMySqlEntityChef.class); + + protected static EntitySpec<? extends Entity> specBase() { + EntitySpec<ChefEntity> spec = EntitySpec.create(ChefEntity.class); + + ChefConfigs.addToLaunchRunList(spec, "mysql::server"); + spec.configure(PID_FILE, "/var/run/mysqld/mysql*.pid"); + // init.d service name is sometimes mysql, sometimes mysqld, depending ubuntu/centos + // we use pid file above instead, but this (with the right name) could be used: +// spec.configure(SERVICE_NAME, "mysql"); + + // chef mysql fails on first run but works on second if switching between server and solo modes + spec.configure(ChefConfig.CHEF_RUN_CONVERGE_TWICE, true); + + // only used for solo, but safely ignored for knife + ChefConfigs.addToCookbooksFromGithub(spec, "mysql", "build-essential", "openssl"); + // we always need dependent cookbooks set, and mysql requires password set + // (TODO for knife we might wish to prefer things from the server) + ChefConfigs.addLaunchAttributes(spec, MutableMap.of("mysql", + MutableMap.of() + .add("server_root_password", "MyPassword") + .add("server_debian_password", "MyPassword") + .add("server_repl_password", "MyPassword") + )); + + return spec; + } + + public static EntitySpec<? extends Entity> spec() { + EntitySpec<? extends Entity> spec = specBase(); + log.debug("Created entity spec for MySql: "+spec); + return spec; + } + + public static EntitySpec<? extends Entity> specSolo() { + EntitySpec<? extends Entity> spec = specBase(); + spec.configure(ChefConfig.CHEF_MODE, ChefConfig.ChefModes.SOLO); + log.debug("Created entity spec for MySql: "+spec); + return spec; + } + + public static EntitySpec<? extends Entity> specKnife() { + EntitySpec<? extends Entity> spec = specBase(); + spec.configure(ChefConfig.CHEF_MODE, ChefConfig.ChefModes.KNIFE); + log.debug("Created entity spec for MySql: "+spec); + return spec; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/TypedToyMySqlEntityChef.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/TypedToyMySqlEntityChef.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/TypedToyMySqlEntityChef.java new file mode 100644 index 0000000..44a622a --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/TypedToyMySqlEntityChef.java @@ -0,0 +1,55 @@ +/* + * 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.entity.chef.mysql; + +import org.apache.brooklyn.entity.chef.ChefConfig; +import org.apache.brooklyn.entity.chef.ChefEntityImpl; +import org.apache.brooklyn.util.git.GithubUrls; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +/** Illustrates how to define an entity using Java as a Java class, extending ChefEntityImpl */ +public class TypedToyMySqlEntityChef extends ChefEntityImpl { + + @Override + public void init() { + super.init(); + + String password = "p4ssw0rd"; + + setConfig(CHEF_COOKBOOK_PRIMARY_NAME, "mysql"); + setConfig(CHEF_COOKBOOK_URLS, ImmutableMap.of( + "mysql", GithubUrls.tgz("opscode-cookbooks", "mysql", "v4.0.12"), + "openssl", GithubUrls.tgz("opscode-cookbooks", "openssl", "v1.1.0"), + "mysql", GithubUrls.tgz("opscode-cookbooks", "build-essential", "v1.4.4"))); + + setConfig(CHEF_LAUNCH_RUN_LIST, ImmutableSet.of("mysql::server")); + setConfig(CHEF_LAUNCH_ATTRIBUTES, ImmutableMap.<String,Object>of( + "mysql", ImmutableMap.of( + "server_root_password", password, + "server_repl_password", password, + "server_debian_password", password))); + + setConfig(ChefConfig.PID_FILE, "/var/run/mysqld/mysqld.pid"); + + setConfig(CHEF_MODE, ChefModes.SOLO); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/java/EntityPollingTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/EntityPollingTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/EntityPollingTest.java new file mode 100644 index 0000000..b49e235 --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/EntityPollingTest.java @@ -0,0 +1,206 @@ +/* + * 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.entity.java; + +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.location.MachineLocation; +import org.apache.brooklyn.core.test.entity.TestApplication; +import org.apache.brooklyn.entity.core.Entities; +import org.apache.brooklyn.entity.java.UsesJmx; +import org.apache.brooklyn.entity.java.VanillaJavaAppImpl; +import org.apache.brooklyn.entity.java.VanillaJavaAppSshDriver; +import org.apache.brooklyn.entity.java.UsesJmx.JmxAgentModes; +import org.apache.brooklyn.entity.software.base.SoftwareProcess; +import org.apache.brooklyn.entity.software.base.test.jmx.JmxService; +import org.apache.brooklyn.sensor.core.BasicAttributeSensor; +import org.apache.brooklyn.sensor.feed.jmx.JmxAttributePollConfig; +import org.apache.brooklyn.sensor.feed.jmx.JmxFeed; +import org.apache.brooklyn.test.EntityTestUtils; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.apache.brooklyn.location.basic.SshMachineLocation; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +public class EntityPollingTest { + + private static final Logger LOG = LoggerFactory.getLogger(EntityPollingTest.class); + + private JmxService jmxService; + private TestApplication app; + private SoftwareProcess entity; + + private static final BasicAttributeSensor<String> stringAttribute = new BasicAttributeSensor<String>( + String.class, "brooklyn.test.stringAttribute", "Brooklyn testing int attribute"); + private String objectName = "Brooklyn:type=MyTestMBean,name=myname"; + + private static final ObjectName jmxObjectName; + static { + try { + jmxObjectName = new ObjectName("Brooklyn:type=MyTestMBean,name=myname"); + } catch (MalformedObjectNameException e) { + throw Exceptions.propagate(e); + } + } + + private static final String attributeName = "myattrib"; + + public static class SubVanillaJavaApp extends VanillaJavaAppImpl { + private JmxFeed feed; + + @Override protected void connectSensors() { + super.connectSensors(); + + // Add a sensor that we can explicitly set in jmx + feed = JmxFeed.builder() + .entity(this) + .pollAttribute(new JmxAttributePollConfig<String>(stringAttribute) + .objectName(jmxObjectName) + .attributeName(attributeName)) + .build(); + } + + @Override + public void disconnectSensors() { + super.disconnectSensors(); + if (feed != null) feed.stop(); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public Class getDriverInterface() { + return null; + } + + @Override + public VanillaJavaAppSshDriver newDriver(MachineLocation loc) { + return new VanillaJavaAppSshDriver(this, (SshMachineLocation)loc) { + @Override public void install() { + // no-op + } + @Override public void customize() { + // no-op + } + @Override public void launch() { + // no-op + } + @Override public boolean isRunning() { + return true; + } + @Override public void stop() { + // no-op + } + @Override public void kill() { + // no-op + } + }; + } + }; + + @BeforeMethod(alwaysRun=true) + public void setUp() { + app = TestApplication.Factory.newManagedInstanceForTests(); + + /* + * Create an entity, using real entity code, but that swaps out the external process + * for a JmxService that we can control in the test. + */ + entity = app.createAndManageChild(EntitySpec.create(SoftwareProcess.class).impl(SubVanillaJavaApp.class) + .configure("rmiRegistryPort", 40123) + .configure("mxbeanStatsEnabled", false) + .configure(UsesJmx.JMX_AGENT_MODE, JmxAgentModes.JMX_RMI_CUSTOM_AGENT)); + } + + @AfterMethod(alwaysRun=true) + public void tearDown() throws Exception { + if (app != null) Entities.destroyAll(app.getManagementContext()); + if (jmxService != null) jmxService.shutdown(); + } + + // Tests that the happy path works + @Test(groups="Integration") + public void testSimpleConnection() throws Exception { + jmxService = new JmxService("localhost", 40123); + jmxService.registerMBean(ImmutableMap.of(attributeName, "myval"), objectName); + + app.start(ImmutableList.of(new SshMachineLocation(MutableMap.of("address", "localhost")))); + + // Starts with value defined when registering... + EntityTestUtils.assertAttributeEqualsEventually(entity, stringAttribute, "myval"); + } + + // Test that connect will keep retrying (e.g. start script returns before the JMX server is up) + @Test(groups="Integration") + public void testEntityWithDelayedJmxStartupWillKeepRetrying() { + // In 2 seconds time, we'll start the JMX server + Thread t = new Thread(new Runnable() { + public void run() { + try { + Thread.sleep(2000); + jmxService = new JmxService("localhost", 40123); + jmxService.registerMBean(ImmutableMap.of(attributeName, "myval"), objectName); + } catch (Exception e) { + LOG.error("Error in testEntityWithDelayedJmxStartupWillKeepRetrying", e); + throw Exceptions.propagate(e); + } + }}); + + try { + t.start(); + app.start(ImmutableList.of(new SshMachineLocation(MutableMap.of("address", "localhost")))); + + EntityTestUtils.assertAttributeEqualsEventually(entity, stringAttribute, "myval"); + + } finally { + t.interrupt(); + } + } + + @Test(groups="Integration") + public void testJmxConnectionGoesDownRequiringReconnect() throws Exception { + jmxService = new JmxService("localhost", 40123); + jmxService.registerMBean(ImmutableMap.of(attributeName, "myval"), objectName); + + app.start(ImmutableList.of(new SshMachineLocation(MutableMap.of("address", "localhost")))); + + EntityTestUtils.assertAttributeEqualsEventually(entity, stringAttribute, "myval"); + + // Shutdown the MBeanServer - simulates network failure so can't connect + jmxService.shutdown(); + + // TODO Want a better way of determining that the entity is down; ideally should have + // sensor for entity-down that's wired up to a JMX attribute? + Thread.sleep(5000); + + // Restart MBeanServer, and set attribute to different value; expect it to be polled again + jmxService = new JmxService("localhost", 40123); + jmxService.registerMBean(ImmutableMap.of(attributeName, "myval2"), objectName); + + EntityTestUtils.assertAttributeEqualsEventually(entity, stringAttribute, "myval2"); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/java/ExampleVanillaMain.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/ExampleVanillaMain.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/ExampleVanillaMain.java new file mode 100644 index 0000000..114d45d --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/ExampleVanillaMain.java @@ -0,0 +1,26 @@ +/* + * 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.entity.java; + +public class ExampleVanillaMain { + public static void main(String[] args) throws Exception { + System.out.println("In VanillaJavaExampleMain.main"); + Thread.sleep(100*1000); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/java/ExampleVanillaMainCpuHungry.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/ExampleVanillaMainCpuHungry.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/ExampleVanillaMainCpuHungry.java new file mode 100644 index 0000000..e45be44 --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/ExampleVanillaMainCpuHungry.java @@ -0,0 +1,41 @@ +/* + * 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.entity.java; + +public class ExampleVanillaMainCpuHungry { + private static final int MAX_TIME_MILLIS = 100*1000; + private static final int CALCULATIONS_PER_CYCLE = 100000; + private static final int SLEEP_PER_CYCLE_MILLIS = 1; + + public static void main(String[] args) throws Exception { + System.out.println("In ExampleVanillaMainCpuHungry.main"); + long startTime = System.currentTimeMillis(); + long count = 0; + double total = 0; + do { + for (int i = 0; i < CALCULATIONS_PER_CYCLE; i++) { + total += Math.sqrt(Math.random()); + count++; + } + Thread.sleep(SLEEP_PER_CYCLE_MILLIS); + } while ((System.currentTimeMillis() - startTime) < MAX_TIME_MILLIS); + + System.out.println("Did "+count+" random square roots, took "+(System.currentTimeMillis()-startTime)+"ms; total = "+total); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/java/JavaOptsTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/JavaOptsTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/JavaOptsTest.java new file mode 100644 index 0000000..a9da253 --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/JavaOptsTest.java @@ -0,0 +1,361 @@ +/* + * 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.entity.java; + +import static org.testng.Assert.fail; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.location.LocationSpec; +import org.apache.brooklyn.api.location.MachineLocation; +import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; +import org.apache.brooklyn.entity.java.UsesJmx; +import org.apache.brooklyn.entity.java.VanillaJavaApp; +import org.apache.brooklyn.entity.java.VanillaJavaAppImpl; +import org.apache.brooklyn.entity.java.VanillaJavaAppSshDriver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.apache.brooklyn.location.basic.SshMachineLocation; +import org.apache.brooklyn.util.collections.MutableList; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.collections.MutableSet; +import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool; +import org.apache.brooklyn.util.core.internal.ssh.SshTool; +import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool.ExecCmd; +import org.apache.brooklyn.util.jmx.jmxmp.JmxmpAgent; +import org.apache.brooklyn.util.text.Strings; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.MapDifference.ValueDifference; +import com.google.common.collect.Maps; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class JavaOptsTest extends BrooklynAppUnitTestSupport { + + // TODO Test setting classpath; but this works by customize() copying all the artifacts into /lib/* + // so that we can simply set this on the classpath... + + private static final Logger log = LoggerFactory.getLogger(JavaOptsTest.class); + + private SshMachineLocation loc; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + RecordingSshTool.execScriptCmds.clear(); + super.setUp(); + loc = mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class) + .configure("address", "localhost") + .configure(SshTool.PROP_TOOL_CLASS, RecordingSshTool.class.getName())); + } + + @AfterMethod(alwaysRun=true) + @Override + public void tearDown() throws Exception { + super.tearDown(); + RecordingSshTool.execScriptCmds.clear(); + } + + @Test + public void testSimpleLaunchesJavaProcess() { + VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class) + .configure("main", "my.Main").configure("useJmx", false)); + app.start(ImmutableList.of(loc)); + + String runDir = javaProcess.getRunDir(); + Map<String,String> expectedEnvs = ImmutableMap.<String,String>of(); + List<String> expectedCmds = ImmutableList.of(String.format("java $JAVA_OPTS -cp \"%1$s/lib\" my.Main >> %1$s/console 2>&1 </dev/null &", runDir)); + + assertHasExpectedCmds(expectedCmds, expectedEnvs); + } + + @Test + public void testPassesJavaArgs() { + VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class) + .configure("main", "my.Main").configure("useJmx", false).configure("args", ImmutableList.of("a1", "a2"))); + app.start(ImmutableList.of(loc)); + + String runDir = javaProcess.getRunDir(); + Map<String,String> expectedEnvs = ImmutableMap.<String,String>of(); + List<String> expectedCmds = ImmutableList.of(String.format("java $JAVA_OPTS -cp \"%1$s/lib\" my.Main \"a1\" \"a2\" >> %1$s/console 2>&1 </dev/null &", runDir)); + + assertHasExpectedCmds(expectedCmds, expectedEnvs); + } + + @Test + public void testPassesJavaOpts() { + VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class) + .configure("main", "my.Main").configure("useJmx", false).configure("javaOpts", ImmutableList.of("-abc"))); + app.start(ImmutableList.of(loc)); + + String runDir = javaProcess.getRunDir(); + String defaultJavaOpts = "-Xms128m -Xmx512m -XX:MaxPermSize=512m"; + String expectedJavaOpts = defaultJavaOpts+" -abc"; + Map<String,String> expectedEnvs = ImmutableMap.<String,String>of("JAVA_OPTS", expectedJavaOpts); + List<String> expectedCmds = ImmutableList.of(String.format("java $JAVA_OPTS -cp \"%1$s/lib\" my.Main >> %1$s/console 2>&1 </dev/null &", runDir)); + + assertHasExpectedCmds(expectedCmds, expectedEnvs); + } + + @Test + public void testPassesJavaSysProps() { + VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class) + .configure("main", "my.Main").configure("useJmx", false).configure("javaSysProps", ImmutableMap.of("mykey", "myval"))); + + app.start(ImmutableList.of(loc)); + + String runDir = javaProcess.getRunDir(); + String defaultJavaOpts = "-Xms128m -Xmx512m -XX:MaxPermSize=512m"; + String expectedJavaOpts = defaultJavaOpts+" -Dmykey=myval"; + Map<String,String> expectedEnvs = ImmutableMap.<String,String>of("JAVA_OPTS", expectedJavaOpts); + List<String> expectedCmds = ImmutableList.of(String.format("java $JAVA_OPTS -cp \"%1$s/lib\" my.Main >> %1$s/console 2>&1 </dev/null &", runDir)); + + assertHasExpectedCmds(expectedCmds, expectedEnvs); + } + + @Test + public void testPassesJavaOptsOverridingDefaults() { + VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class) + .configure("main", "my.Main").configure("useJmx", false).configure("javaOpts", ImmutableList.of("-Xmx567m", "-XX:MaxPermSize=567m"))); + app.start(ImmutableList.of(loc)); + + String runDir = javaProcess.getRunDir(); + Object expectedJavaOpts = MutableSet.of("-Xms128m", "-Xmx567m", "-XX:MaxPermSize=567m"); + Map<String,Object> expectedEnvs = ImmutableMap.<String,Object>of("JAVA_OPTS", expectedJavaOpts); + List<String> expectedCmds = ImmutableList.of(String.format("java $JAVA_OPTS -cp \"%1$s/lib\" my.Main >> %1$s/console 2>&1 </dev/null &", runDir)); + + assertHasExpectedCmds(expectedCmds, expectedEnvs); + } + + public static class TestingJavaOptsVanillaJavaAppImpl extends VanillaJavaAppImpl { + @Override public VanillaJavaAppSshDriver newDriver(MachineLocation loc) { + return new VanillaJavaAppSshDriver(this, (SshMachineLocation)loc) { + @Override protected List<String> getCustomJavaConfigOptions() { + return MutableList.<String>builder() + .addAll(super.getCustomJavaConfigOptions()) + .add("-server") + .build(); + }; + }; + } + } + + @Test + public void testPassesJavaOptsObeyingMutualExclusions() { + VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class, TestingJavaOptsVanillaJavaAppImpl.class) + .configure("main", "my.Main").configure("useJmx", false).configure("javaOpts", ImmutableList.of("-client"))); + app.start(ImmutableList.of(loc)); + + String runDir = javaProcess.getRunDir(); + String defaultJavaOpts = "-Xms128m -Xmx512m -XX:MaxPermSize=512m"; + String expectedJavaOpts = defaultJavaOpts+" -client"; + Map<String,String> expectedEnvs = ImmutableMap.<String,String>of("JAVA_OPTS", expectedJavaOpts); + List<String> expectedCmds = ImmutableList.of(String.format("java $JAVA_OPTS -cp \"%1$s/lib\" my.Main >> %1$s/console 2>&1 </dev/null &", runDir)); + + assertHasExpectedCmds(expectedCmds, expectedEnvs); + } + + /** + * Asserts that one of the scripts executed had these commands, and these environment variables. + * There could be other commands in between though. + */ + private void assertHasExpectedCmds(List<String> expectedCmds, Map<String,?> expectedEnvs) { + if (RecordingSshTool.execScriptCmds.isEmpty()) + fail("No commands recorded"); + + for (ExecCmd cmd : RecordingSshTool.execScriptCmds) { + // TODO Check expectedCmds in-order + + // check if expectedEnv is a set, then the string value contains all elements in the set + Map<Object, ValueDifference<Object>> difference = Maps.<Object,Object>difference(cmd.env, expectedEnvs).entriesDiffering(); + boolean same = difference.isEmpty(); + if (!same) { + Set<Object> differingKeys = new LinkedHashSet<Object>(difference.keySet()); + Iterator<Object> ki = differingKeys.iterator(); + while (ki.hasNext()) { + Object key = ki.next(); + Object expectationHere = expectedEnvs.get(key); + Object valueHere = cmd.env.get(key); + if (valueHere==null) break; + + if (expectationHere instanceof Set) { + Set mutableExpectationHere = new LinkedHashSet(((Set)expectationHere)); + Iterator si = ((Set)mutableExpectationHere).iterator(); + while (si.hasNext()) { + Object oneExpectationHere = si.next(); + if (valueHere.toString().contains(Strings.toString(oneExpectationHere))) + si.remove(); + else break; + } + if (mutableExpectationHere.isEmpty()) + differingKeys.remove(key); + else + // not the same + break; + } else { + // not the same + break; + } + } + if (differingKeys.isEmpty()) + same = true; + } + + if (cmd.commands.containsAll(expectedCmds) && same) { + return; + } + } + + for (ExecCmd cmd : RecordingSshTool.execScriptCmds) { + log.info("Command:"); + log.info("\tEnv:"); + for (Map.Entry<?,?> entry : cmd.env.entrySet()) { + log.info("\t\t"+entry.getKey()+" = "+entry.getValue()); + } + log.info("\tCmds:"); + for (String c : cmd.commands) { + log.info("\t\t"+c); + } + } + + fail("Cmd not present: expected="+expectedCmds+"/"+expectedEnvs+"; actual="+RecordingSshTool.execScriptCmds); + } + + public static class TestingNoSensorsVanillaJavaAppImpl extends VanillaJavaAppImpl { + protected void connectSensors() { + /* nothing here */ + setAttribute(SERVICE_UP, true); + } + } + + private void assertJmxWithPropsHasPhrases(Map props, + List<String> expectedPhrases, + List<String> forbiddenPhrases) { + if (!props.containsKey("main")) props.put("main", "my.Main"); + @SuppressWarnings({ "unused" }) + VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class, TestingNoSensorsVanillaJavaAppImpl.class) + .configure(props)); + app.start(ImmutableList.of(loc)); + + List<String> phrases = new ArrayList<String>(expectedPhrases); + Set<String> forbiddenPhrasesFound = new LinkedHashSet<String>(); + for (ExecCmd cmd : RecordingSshTool.execScriptCmds) { + String biggun = ""+cmd.env+" "+cmd.commands; + Iterator<String> pi = phrases.iterator(); + while (pi.hasNext()) { + String phrase = pi.next(); + if (biggun.contains(phrase)) pi.remove(); + } + if (forbiddenPhrases!=null) + for (String p: forbiddenPhrases) + if (biggun.contains(p)) forbiddenPhrasesFound.add(p); + } + + if (!phrases.isEmpty()) { + log.warn("Missing phrases in commands: "+phrases+"\nCOMMANDS: "+RecordingSshTool.execScriptCmds); + fail("Missing phrases in commands: "+phrases); + } + if (!forbiddenPhrasesFound.isEmpty()) { + log.warn("Forbidden phrases found in commands: "+forbiddenPhrasesFound+"\nCOMMANDS: "+RecordingSshTool.execScriptCmds); + fail("Forbidden phrases found in commands: "+forbiddenPhrasesFound); + } + } + + private static final List<String> EXPECTED_BASIC_JMX_OPTS = Arrays.asList( + "-Dcom.sun.management.jmxremote", + "-Dcom.sun.management.jmxremote.ssl=false", + "-Dcom.sun.management.jmxremote.authenticate=false" + ); + + private static final List<String> FORBIDDEN_BASIC_JMX_OPTS = Arrays.asList( + "-Dcom.sun.management.jmxremote.ssl=true", + + // often breaks things, as this is an advertised hostname usually; + // it typically listens on all interfaces anyway + "-Djava.rmi.server.hostname=0.0.0.0" + ); + + @Test + public void testBasicJmxFromFlag() { + assertJmxWithPropsHasPhrases( + MutableMap.builder(). + put("useJmx", true). + build(), + EXPECTED_BASIC_JMX_OPTS, + FORBIDDEN_BASIC_JMX_OPTS); + } + + @Test + public void testBasicJmxFromConfig() { + assertJmxWithPropsHasPhrases( + MutableMap.builder(). + put(UsesJmx.USE_JMX, true). + build(), + EXPECTED_BASIC_JMX_OPTS, + FORBIDDEN_BASIC_JMX_OPTS); + } + + @Test + public void testBasicJmxConfigFromDefault() { + assertJmxWithPropsHasPhrases( + MutableMap.builder(). + build(), + EXPECTED_BASIC_JMX_OPTS, + FORBIDDEN_BASIC_JMX_OPTS); + } + + @Test + public void testSecureJmxConfigFromDefault() { + final List<String> EXPECTED_SECURE_JMX_OPTS = Arrays.asList( + "-Dcom.sun.management.jmxremote", + "-Dbrooklyn.jmxmp.port=31009", + "-Dcom.sun.management.jmxremote.ssl=true", + "-D"+JmxmpAgent.AUTHENTICATE_CLIENTS_PROPERTY+"=true", + "keyStore", "/jmx-keystore", + "trustStore", "/jmx-truststore", + "-javaagent", "brooklyn-jmxmp-agent" + ); + + final List<String> FORBIDDEN_SECURE_JMX_OPTS = Arrays.asList( + "-Dcom.sun.management.jmxremote.authenticate=true", + "-Dcom.sun.management.jmxremote.ssl=false", + // hostname isn't forbidden -- but it is generally not used now + "-Djava.rmi.server.hostname=" + ); + + assertJmxWithPropsHasPhrases( + MutableMap.builder() + .put(UsesJmx.JMX_SSL_ENABLED, true) + .put(UsesJmx.JMX_PORT, 31009) + .build(), + EXPECTED_SECURE_JMX_OPTS, + FORBIDDEN_SECURE_JMX_OPTS); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/java/JavaSoftwareProcessSshDriverIntegrationTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/JavaSoftwareProcessSshDriverIntegrationTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/JavaSoftwareProcessSshDriverIntegrationTest.java new file mode 100644 index 0000000..68cdc61 --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/JavaSoftwareProcessSshDriverIntegrationTest.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.entity.java; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.internal.EntityLocal; +import org.apache.brooklyn.api.location.LocationSpec; +import org.apache.brooklyn.api.location.MachineProvisioningLocation; +import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport; +import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests; +import org.apache.brooklyn.entity.core.BrooklynConfigKeys; +import org.apache.brooklyn.entity.java.JavaSoftwareProcessSshDriver; +import org.apache.brooklyn.entity.software.base.SoftwareProcess; +import org.apache.brooklyn.entity.software.base.lifecycle.MyEntity; +import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.os.Os; +import org.apache.brooklyn.util.text.Strings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.apache.brooklyn.location.basic.SshMachineLocation; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; + +public class JavaSoftwareProcessSshDriverIntegrationTest extends BrooklynAppLiveTestSupport { + + private static final long TIMEOUT_MS = 10 * 1000; + + private static final Logger LOG = LoggerFactory.getLogger(JavaSoftwareProcessSshDriverIntegrationTest.class); + + private MachineProvisioningLocation<?> localhost; + + private static class ConcreteJavaSoftwareProcessSshDriver extends JavaSoftwareProcessSshDriver { + public ConcreteJavaSoftwareProcessSshDriver(EntityLocal entity, SshMachineLocation machine) { + super(entity, machine); + } + @Override protected String getLogFileLocation() { return null; } + @Override public boolean isRunning() { return false; } + @Override public void stop() {} + @Override public void install() {} + @Override public void customize() {} + @Override public void launch() {} + } + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + localhost = app.newLocalhostProvisioningLocation(); + } + + @Test(groups = "Integration") + public void testJavaStartStopSshDriverStartsAndStopsApp() throws Exception { + final MyEntity entity = app.createAndManageChild(EntitySpec.create(MyEntity.class)); + app.start(ImmutableList.of(localhost)); + Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() { + public void run() { + assertTrue(entity.getAttribute(SoftwareProcess.SERVICE_UP)); + }}); + + entity.stop(); + assertFalse(entity.getAttribute(SoftwareProcess.SERVICE_UP)); + } + + @Test(groups = "Integration") + public void testGetJavaVersion() throws Exception { + SshMachineLocation sshLocation = app.getManagementContext().getLocationManager().createLocation( + LocationSpec.create(SshMachineLocation.class).configure("address", "localhost")); + JavaSoftwareProcessSshDriver driver = new ConcreteJavaSoftwareProcessSshDriver(app, sshLocation); + Optional<String> version = driver.getInstalledJavaVersion(); + assertNotNull(version); + assertTrue(version.isPresent()); + LOG.info("{}.testGetJavaVersion found: {} on localhost", getClass(), version.get()); + } + + @Test(groups = "Integration") + public void testStartsInMgmtSpecifiedDirectory() throws Exception { + String dir = Os.mergePathsUnix(Os.tmp(), "/brooklyn-test-"+Strings.makeRandomId(4)); + tearDown(); + mgmt = new LocalManagementContextForTests(); + mgmt.getBrooklynProperties().put(BrooklynConfigKeys.ONBOX_BASE_DIR, dir); + setUp(); + + doTestSpecifiedDirectory(dir, dir); + Os.deleteRecursively(dir); + } + + @Test(groups = "Integration") + public void testStartsInAppSpecifiedDirectoryUnderHome() throws Exception { + String dir = Os.mergePathsUnix("~/.brooklyn-test-"+Strings.makeRandomId(4)); + try { + app.config().set(BrooklynConfigKeys.ONBOX_BASE_DIR, dir); + doTestSpecifiedDirectory(dir, dir); + } finally { + Os.deleteRecursively(dir); + } + } + + @Test(groups = "Integration") + public void testStartsInDifferentRunAndInstallSpecifiedDirectories() throws Exception { + String dir1 = Os.mergePathsUnix(Os.tmp(), "/brooklyn-test-"+Strings.makeRandomId(4)); + String dir2 = Os.mergePathsUnix(Os.tmp(), "/brooklyn-test-"+Strings.makeRandomId(4)); + app.config().set(BrooklynConfigKeys.INSTALL_DIR, dir1); + app.config().set(BrooklynConfigKeys.RUN_DIR, dir2); + doTestSpecifiedDirectory(dir1, dir2); + Os.deleteRecursively(dir1); + Os.deleteRecursively(dir2); + } + + @Test(groups = "Integration") + public void testStartsInLegacySpecifiedDirectory() throws Exception { + String dir1 = Os.mergePathsUnix(Os.tmp(), "/brooklyn-test-"+Strings.makeRandomId(4)); + String dir2 = Os.mergePathsUnix(Os.tmp(), "/brooklyn-test-"+Strings.makeRandomId(4)); + tearDown(); + mgmt = new LocalManagementContextForTests(); + mgmt.getBrooklynProperties().put("brooklyn.dirs.install", dir1); + mgmt.getBrooklynProperties().put("brooklyn.dirs.run", dir2); + setUp(); + + app.config().set(BrooklynConfigKeys.RUN_DIR, dir2); + doTestSpecifiedDirectory(dir1, dir2); + Os.deleteRecursively(dir1); + Os.deleteRecursively(dir2); + } + + protected void doTestSpecifiedDirectory(final String installDirPrefix, final String runDirPrefix) throws Exception { + final MyEntity entity = app.createAndManageChild(EntitySpec.create(MyEntity.class)); + app.start(ImmutableList.of(localhost)); + Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() { + public void run() { + assertTrue(entity.getAttribute(SoftwareProcess.SERVICE_UP)); + + String installDir = entity.getAttribute(SoftwareProcess.INSTALL_DIR); + Assert.assertNotNull(installDir); + + String runDir = entity.getAttribute(SoftwareProcess.RUN_DIR); + Assert.assertNotNull(runDir); + }}); + + String installDir = entity.getAttribute(SoftwareProcess.INSTALL_DIR); + String runDir = entity.getAttribute(SoftwareProcess.RUN_DIR); + LOG.info("dirs for " + app + " are: install=" + installDir + ", run=" + runDir); + assertTrue(installDir.startsWith(Os.tidyPath(installDirPrefix)), "INSTALL_DIR is "+installDir+", does not start with expected prefix "+installDirPrefix); + assertTrue(runDir.startsWith(Os.tidyPath(runDirPrefix)), "RUN_DIR is "+runDir+", does not start with expected prefix "+runDirPrefix); + + entity.stop(); + assertFalse(entity.getAttribute(SoftwareProcess.SERVICE_UP)); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/java/JmxSupportTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/JmxSupportTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/JmxSupportTest.java new file mode 100644 index 0000000..39d6089 --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/JmxSupportTest.java @@ -0,0 +1,135 @@ +/* + * 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.entity.java; + +import static org.testng.Assert.assertEquals; + +import org.apache.brooklyn.core.test.entity.TestApplication; +import org.apache.brooklyn.entity.core.Entities; +import org.apache.brooklyn.entity.java.JmxSupport; +import org.apache.brooklyn.entity.java.UsesJmx.JmxAgentModes; +import org.apache.brooklyn.util.core.ResourceUtils; +import org.apache.brooklyn.util.core.flags.TypeCoercions; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.javalang.JavaClassNames; +import org.apache.brooklyn.util.maven.MavenRetriever; +import org.apache.brooklyn.util.stream.Streams; +import org.apache.brooklyn.util.text.Strings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.Test; + +@Test +public class JmxSupportTest { + + private static final Logger log = LoggerFactory.getLogger(JmxSupportTest.class); + + private TestApplication app; + + @AfterMethod(alwaysRun = true) + public void tearDown() { + if (app!=null) Entities.destroyAll(app.getManagementContext()); + } + + // defaults to JMXMP for most locations (or, in this case, if it does not yet know the location) + public void testJmxAutodetect() { + app = TestApplication.Factory.newManagedInstanceForTests(); + JmxSupport support = new JmxSupport(app, null); + + Assert.assertEquals(support.getJmxAgentMode(), JmxAgentModes.JMXMP_AND_RMI); + } + + public void testJmxmpJarExistence() { + app = TestApplication.Factory.newManagedInstanceForTests(); + app.setConfig(JmxSupport.JMX_AGENT_MODE, JmxAgentModes.JMXMP); + JmxSupport support = new JmxSupport(app, null); + + Assert.assertEquals(support.getJmxAgentJarMavenArtifact().getArtifactId(), + "brooklyn-jmxmp-agent"); + + Assert.assertTrue(ResourceUtils.create(this).doesUrlExist(support.getJmxAgentJarUrl()), support.getJmxAgentJarUrl()); + Assert.assertTrue(support.getJmxAgentJarUrl().contains("-shaded-"), support.getJmxAgentJarUrl()); + } + + public void testJmxrmiJarExistence() { + app = TestApplication.Factory.newManagedInstanceForTests(); + JmxSupport support = new JmxSupport(app, null); + app.setConfig(JmxSupport.JMX_AGENT_MODE, JmxAgentModes.JMX_RMI_CUSTOM_AGENT); + + Assert.assertEquals(support.getJmxAgentJarMavenArtifact().getArtifactId(), + "brooklyn-jmxrmi-agent"); + + Assert.assertTrue(ResourceUtils.create(this).doesUrlExist(support.getJmxAgentJarUrl()), support.getJmxAgentJarUrl()); + } + + @Test + public void testCoerceStringtoJmxAgentModes() { + // Test coercions + assertEquals(TypeCoercions.coerce("AUTODETECT", JmxAgentModes.class), JmxAgentModes.AUTODETECT); + assertEquals(TypeCoercions.coerce("JMXMP_AND_RMI", JmxAgentModes.class), JmxAgentModes.JMXMP_AND_RMI); + assertEquals(TypeCoercions.coerce("JMX_RMI_CUSTOM_AGENT", JmxAgentModes.class), JmxAgentModes.JMX_RMI_CUSTOM_AGENT); + + // Test different case format options + assertEquals(TypeCoercions.coerce("jmxRmiCustomAgent", JmxAgentModes.class), JmxAgentModes.JMX_RMI_CUSTOM_AGENT); + assertEquals(TypeCoercions.coerce("jmx_rmi_custom_agent", JmxAgentModes.class), JmxAgentModes.JMX_RMI_CUSTOM_AGENT); + assertEquals(TypeCoercions.coerce("jmx-rmi-custom-agent", JmxAgentModes.class), JmxAgentModes.JMX_RMI_CUSTOM_AGENT); + assertEquals(TypeCoercions.coerce("JmxRmiCustomAgent", JmxAgentModes.class), JmxAgentModes.JMX_RMI_CUSTOM_AGENT); + } + + + @Test(groups="Integration") + public void testJmxmpJarHostedValidity() { + app = TestApplication.Factory.newManagedInstanceForTests(); + app.setConfig(JmxSupport.JMX_AGENT_MODE, JmxAgentModes.JMXMP); + JmxSupport support = new JmxSupport(app, null); + + // make sure we get a valid jar, big enough (no redirect, and classifier correclty set for this!) + // (we don't want the unshaded jar, that would be no good!) + checkValidArchive(MavenRetriever.hostedUrl(support.getJmxAgentJarMavenArtifact()), 100*1000); + } + + @Test(groups="Integration") + public void testJmxrmiJarHostedValidity() { + app = TestApplication.Factory.newManagedInstanceForTests(); + JmxSupport support = new JmxSupport(app, null); + app.setConfig(JmxSupport.JMX_AGENT_MODE, JmxAgentModes.JMX_RMI_CUSTOM_AGENT); + + // make sure we get a valid jar, big enough (no redirect) + checkValidArchive(MavenRetriever.hostedUrl(support.getJmxAgentJarMavenArtifact()), 4000); + } + + private void checkValidArchive(String url, long minSize) { + byte[] bytes; + try { + bytes = Streams.readFully(ResourceUtils.create(this).getResourceFromUrl(url)); + log.info("read "+bytes.length+" bytes from "+url+" for "+JavaClassNames.callerNiceClassAndMethod(1)); + } catch (Exception e) { + log.warn("Unable to read URL "+url+" for " +JavaClassNames.callerNiceClassAndMethod(1)+ + "; this test may require hosted (sonatype/mavencentral) repo to be populated"); + Assert.fail("Unable to read URL "+url+"; this test may require hosted (sonatype/mavencentral) repo to be populated"); + throw Exceptions.propagate(e); + } + // confirm this follow redirects! + Assert.assertTrue(bytes.length > minSize, "download of "+url+" is suspect ("+Strings.makeSizeString(bytes.length)+")"); + // (could also check it is a zip etc) + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/java/SslKeyConfigTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/SslKeyConfigTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/SslKeyConfigTest.java new file mode 100644 index 0000000..24ce2c9 --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/SslKeyConfigTest.java @@ -0,0 +1,53 @@ +/* + * 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.entity.java; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.security.Key; +import java.security.KeyStore; +import java.security.cert.Certificate; + +import org.apache.brooklyn.util.core.crypto.FluentKeySigner; +import org.apache.brooklyn.util.core.crypto.SecureKeys; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class SslKeyConfigTest { + + @Test + public void testWriteKeyAndCertThenReadThem() throws Exception { + FluentKeySigner signer = new FluentKeySigner("brooklyn-test").selfsign(); + + KeyStore ks = SecureKeys.newKeyStore(); + ks.setKeyEntry("key1", + signer.getKey().getPrivate(), "s3cr3t".toCharArray(), new Certificate[] { signer.getAuthorityCertificate() }); + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + ks.store(bytes, "5t0r3".toCharArray()); + + KeyStore ks2 = SecureKeys.newKeyStore(new ByteArrayInputStream(bytes.toByteArray()), "5t0r3"); + String firstAlias = ks2.aliases().nextElement(); + Assert.assertEquals(firstAlias, "key1"); + Key k = ks2.getKey(firstAlias, "s3cr3t".toCharArray()); + Assert.assertEquals(k, signer.getKey().getPrivate()); + Certificate[] cc = ks2.getCertificateChain(firstAlias); + Assert.assertEquals(cc, new Certificate[] { signer.getAuthorityCertificate() }); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/java/VanillaJavaAppRebindTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/VanillaJavaAppRebindTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/VanillaJavaAppRebindTest.java new file mode 100644 index 0000000..d3a97d3 --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/VanillaJavaAppRebindTest.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.entity.java; + +import static org.testng.Assert.assertTrue; + +import java.io.File; + +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.sensor.AttributeSensor; +import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext; +import org.apache.brooklyn.core.mgmt.rebind.RebindTestUtils; +import org.apache.brooklyn.core.test.entity.TestApplication; +import org.apache.brooklyn.entity.core.Entities; +import org.apache.brooklyn.entity.java.VanillaJavaApp; +import org.apache.brooklyn.entity.java.VanillaJavaAppImpl; +import org.apache.brooklyn.entity.java.JavaOptsTest.TestingJavaOptsVanillaJavaAppImpl; +import org.apache.brooklyn.policy.enricher.RollingTimeWindowMeanEnricher; +import org.apache.brooklyn.sensor.core.Sensors; +import org.apache.brooklyn.test.EntityTestUtils; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.core.ResourceUtils; +import org.apache.brooklyn.util.time.Duration; +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 org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation; + +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.io.Files; + +public class VanillaJavaAppRebindTest { + + private static final Logger LOG = LoggerFactory.getLogger(VanillaJavaAppRebindTest.class); + + private static String BROOKLYN_THIS_CLASSPATH = null; + private static Class<?> MAIN_CLASS = ExampleVanillaMain.class; + + private ClassLoader classLoader = getClass().getClassLoader(); + private LocalManagementContext managementContext; + private File mementoDir; + private TestApplication app; + private LocalhostMachineProvisioningLocation loc; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + mementoDir = Files.createTempDir(); + managementContext = RebindTestUtils.newPersistingManagementContext(mementoDir, classLoader); + + if (BROOKLYN_THIS_CLASSPATH==null) { + BROOKLYN_THIS_CLASSPATH = ResourceUtils.create(MAIN_CLASS).getClassLoaderDir(); + } + app = TestApplication.Factory.newManagedInstanceForTests(managementContext); + loc = app.newLocalhostProvisioningLocation(MutableMap.of("address", "localhost")); + } + + @AfterMethod(alwaysRun = true) + public void tearDown() throws Exception { + if (app != null) Entities.destroyAll(app.getManagementContext()); + if (mementoDir != null) RebindTestUtils.deleteMementoDir(mementoDir); + } + + private void rebind() throws Exception { + RebindTestUtils.waitForPersisted(app); + managementContext.terminate(); + + app = (TestApplication) RebindTestUtils.rebind(mementoDir, getClass().getClassLoader()); + managementContext = (LocalManagementContext) app.getManagementContext(); + loc = (LocalhostMachineProvisioningLocation) Iterables.get(app.getLocations(), 0, null); + } + + @Test(groups="Integration") + public void testRebindToJavaApp() throws Exception { + VanillaJavaApp javaProcess = app.addChild(EntitySpec.create(VanillaJavaApp.class, TestingJavaOptsVanillaJavaAppImpl.class) + .configure("main", MAIN_CLASS.getCanonicalName()).configure("classpath", ImmutableList.of(BROOKLYN_THIS_CLASSPATH))); + + Entities.manage(javaProcess); + app.start(ImmutableList.of(loc)); + + rebind(); + VanillaJavaApp javaProcess2 = (VanillaJavaApp) Iterables.find(app.getChildren(), Predicates.instanceOf(VanillaJavaApp.class)); + + EntityTestUtils.assertAttributeEqualsEventually(javaProcess2, VanillaJavaApp.SERVICE_UP, true); + } + + @Test(groups="Integration") + public void testRebindToKilledJavaApp() throws Exception { + VanillaJavaApp javaProcess = app.addChild(EntitySpec.create(VanillaJavaApp.class, TestingJavaOptsVanillaJavaAppImpl.class) + .configure("main", MAIN_CLASS.getCanonicalName()).configure("classpath", ImmutableList.of(BROOKLYN_THIS_CLASSPATH))); + Entities.manage(javaProcess); + app.start(ImmutableList.of(loc)); + javaProcess.kill(); + + long starttime = System.currentTimeMillis(); + rebind(); + long rebindTime = System.currentTimeMillis() - starttime; + + VanillaJavaApp javaProcess2 = (VanillaJavaApp) Iterables.find(app.getChildren(), Predicates.instanceOf(VanillaJavaApp.class)); + EntityTestUtils.assertAttributeEqualsEventually(javaProcess2, VanillaJavaApp.SERVICE_UP, false); + + // check that it was quick (previously it hung) + assertTrue(rebindTime < 30*1000, "rebindTime="+rebindTime); + } + + + @Test(groups="Integration") + public void testEnrichersOnRebindJavaApp() throws Exception { + VanillaJavaApp javaProcess = app.addChild(EntitySpec.create(VanillaJavaApp.class, EnrichedVanillaJavaAppImpl.class) + .configure("main", MAIN_CLASS.getCanonicalName()).configure("classpath", ImmutableList.of(BROOKLYN_THIS_CLASSPATH))); + + Entities.manage(javaProcess); + app.start(ImmutableList.of(loc)); + + EntityTestUtils.assertAttributeEventuallyNonNull(javaProcess, EnrichedVanillaJavaAppImpl.AVG1); + EntityTestUtils.assertAttributeEventuallyNonNull(javaProcess, EnrichedVanillaJavaAppImpl.AVG2); + LOG.info("Got avg "+javaProcess.getAttribute(EnrichedVanillaJavaAppImpl.AVG1)); + + rebind(); + VanillaJavaApp javaProcess2 = (VanillaJavaApp) Iterables.find(app.getChildren(), Predicates.instanceOf(VanillaJavaApp.class)); + + // check sensors working + EntityTestUtils.assertAttributeChangesEventually(javaProcess2, EnrichedVanillaJavaAppImpl.PROCESS_CPU_TIME); + LOG.info("Avg now "+javaProcess2.getAttribute(EnrichedVanillaJavaAppImpl.AVG1)); + + // check enrichers are functioning + EntityTestUtils.assertAttributeChangesEventually(javaProcess2, EnrichedVanillaJavaAppImpl.AVG1); + EntityTestUtils.assertAttributeChangesEventually(javaProcess2, EnrichedVanillaJavaAppImpl.AVG2); + LOG.info("Avg now "+javaProcess2.getAttribute(EnrichedVanillaJavaAppImpl.AVG1)); + + // and check we don't have too many + Assert.assertEquals(javaProcess2.getEnrichers().size(), javaProcess.getEnrichers().size()); + } + + public static class EnrichedVanillaJavaAppImpl extends VanillaJavaAppImpl { + private static final AttributeSensor<Double> AVG1 = Sensors.newDoubleSensor("avg1"); + private static final AttributeSensor<Double> AVG2 = Sensors.newDoubleSensor("avg2"); + + @Override + public void onManagementStarted() { + super.onManagementStarted(); + LOG.info("mgmt started for "+this); + addEnricher(new RollingTimeWindowMeanEnricher<Double>(this, PROCESS_CPU_TIME, AVG1, Duration.TEN_SECONDS)); + } + @Override + protected void connectSensors() { + super.connectSensors(); + LOG.info("connecting sensors for "+this); + addEnricher(new RollingTimeWindowMeanEnricher<Double>(this, PROCESS_CPU_TIME, AVG2, Duration.TEN_SECONDS)); + } + } + +}
