/*
* 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.location.jclouds;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import java.net.URI;
import java.util.List;
import java.util.Set;
import org.apache.brooklyn.api.mgmt.ManagementContext;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.internal.BrooklynProperties;
import org.apache.brooklyn.core.mgmt.rebind.RebindTestFixtureWithApp;
import org.apache.brooklyn.core.test.entity.TestApplication;
import org.apache.brooklyn.util.core.config.ConfigBag;
import org.apache.brooklyn.util.exceptions.CompoundRuntimeException;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.RunNodesException;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeMetadata.Status;
import org.jclouds.compute.domain.OperatingSystem;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.domain.Processor;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.Volume;
import org.jclouds.compute.domain.internal.HardwareImpl;
import org.jclouds.compute.domain.internal.NodeMetadataImpl;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.LocationScope;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.domain.internal.LocationImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
/**
* Tests rebind (i.e. restarting Brooklyn server) when there are live JcloudsSshMachineLocation object(s).
*
* It is still a live test because it connects to the Softlayer API for finding images, etc.
* But it does not provision any VMs, so is much faster/cheaper.
*/
public class JcloudsRebindStubTest extends RebindTestFixtureWithApp {
// TODO Duplication of AbstractJcloudsLiveTest, because we're subclassing RebindTestFixture instead.
private static final Logger LOG = LoggerFactory.getLogger(JcloudsRebindStubTest.class);
public static final String SOFTLAYER_LOCATION_SPEC = "jclouds:" + AbstractJcloudsLiveTest.SOFTLAYER_PROVIDER;
public static final String SOFTLAYER_IMAGE_ID = "UBUNTU_14_64";
protected List<ManagementContext> mgmts;
protected Multimap<ManagementContext, JcloudsSshMachineLocation> machines;
protected BrooklynProperties brooklynProperties;
@BeforeMethod(alwaysRun=true)
public void setUp() throws Exception {
super.setUp();
mgmts = Lists.newCopyOnWriteArrayList(ImmutableList.<ManagementContext>of(origManagementContext));
machines = Multimaps.synchronizedMultimap(ArrayListMultimap.<ManagementContext, JcloudsSshMachineLocation>create());
// Don't let any defaults from brooklyn.properties (except credentials) interfere with test
brooklynProperties = origManagementContext.getBrooklynProperties();
AbstractJcloudsLiveTest.stripBrooklynProperties(brooklynProperties);
}
@AfterMethod(alwaysRun=true)
public void tearDown() throws Exception {
List<Exception> exceptions = Lists.newArrayList();
for (ManagementContext mgmt : mgmts) {
try {
if (mgmt.isRunning()) Entities.destroyAll(mgmt);
} catch (Exception e) {
LOG.warn("Error destroying management context", e);
exceptions.add(e);
}
}
mgmts.clear();
origManagementContext = null;
newManagementContext = null;
origApp = null;
newApp = null;
super.tearDown();
if (exceptions.size() > 0) {
throw new CompoundRuntimeException("Error in tearDown of "+getClass(), exceptions);
}
}
@Override
protected boolean useLiveManagementContext() {
return true;
}
@Override
protected TestApplication rebind() throws Exception {
TestApplication result = super.rebind();
mgmts.add(newManagementContext);
return result;
}
@Test(groups={"Live", "Live-sanity"})
public void testRebind() throws Exception {
LocationImpl locImpl = new LocationImpl(
LocationScope.REGION,
"myLocId",
"myLocDescription",
null,
ImmutableList.<String>of(), // iso3166Codes
ImmutableMap.<String,Object>of()); // metadata
NodeMetadata node = new NodeMetadataImpl(
"softlayer",
"myname",
"123", // ids in SoftLayer are numeric
locImpl,
URI.create("http://myuri.com"),
ImmutableMap.<String, String>of(), // userMetadata
ImmutableSet.<String>of(), // tags
"mygroup",
new HardwareImpl(
"myHardwareProviderId",
"myHardwareName",
"myHardwareId",
locImpl,
URI.create("http://myuri.com"),
ImmutableMap.<String, String>of(), // userMetadata
ImmutableSet.<String>of(), // tags
ImmutableList.<Processor>of(),
1024,
ImmutableList.<Volume>of(),
Predicates.<Image>alwaysTrue(), // supportsImage,
(String)null, // hypervisor
false),
SOFTLAYER_IMAGE_ID,
new OperatingSystem(
OsFamily.CENTOS,
"myOsName",
"myOsVersion",
"myOsArch",
"myDescription",
true), // is64Bit
Status.RUNNING,
"myBackendStatus",
22, // login-port
ImmutableList.of("1.2.3.4"), // publicAddresses,
ImmutableList.of("10.2.3.4"), // privateAddresses,
LoginCredentials.builder().identity("myidentity").password("mypassword").build(),
"myHostname");
ByonComputeServiceRegistry computeServiceRegistry = new ByonComputeServiceRegistry(node);
JcloudsLocation origJcloudsLoc = (JcloudsLocation) mgmt().getLocationRegistry().resolve("jclouds:softlayer", ImmutableMap.of(
JcloudsLocation.COMPUTE_SERVICE_REGISTRY, computeServiceRegistry,
JcloudsLocation.WAIT_FOR_SSHABLE, false,
JcloudsLocation.USE_JCLOUDS_SSH_INIT, false));
JcloudsSshMachineLocation origMachine = (JcloudsSshMachineLocation) origJcloudsLoc.obtain(ImmutableMap.of("imageId", SOFTLAYER_IMAGE_ID));
String origHostname = origMachine.getHostname();
NodeMetadata origNode = origMachine.getNode();
Template origTemplate = origMachine.getTemplate();
rebind();
// Check the machine is as before.
// Call to getOptionalNode() will cause it to try to resolve this node in Softlayer; but it won't find it.
JcloudsSshMachineLocation newMachine = (JcloudsSshMachineLocation) newManagementContext.getLocationManager().getLocation(origMachine.getId());
JcloudsLocation newJcloudsLoc = newMachine.getParent();
String newHostname = newMachine.getHostname();
String newNodeId = newMachine.getJcloudsId();
Optional<NodeMetadata> newNode = newMachine.getOptionalNode();
Optional<Template> newTemplate = newMachine.getOptionalTemplate();
assertEquals(newHostname, origHostname);
assertEquals(origNode.getId(), newNodeId);
assertFalse(newNode.isPresent(), "newNode="+newNode);
assertFalse(newTemplate.isPresent(), "newTemplate="+newTemplate);
assertEquals(newJcloudsLoc.getProvider(), origJcloudsLoc.getProvider());
}
protected static class ByonComputeServiceRegistry extends ComputeServiceRegistryImpl implements ComputeServiceRegistry {
private final NodeMetadata node;
ByonComputeServiceRegistry(NodeMetadata node) throws Exception {
this.node = node;
}
@Override
public ComputeService findComputeService(ConfigBag conf, boolean allowReuse) {
ComputeService delegate = super.findComputeService(conf, allowReuse);
return new StubComputeService(delegate, node);
}
}
static class StubComputeService extends DelegatingComputeService {
private final NodeMetadata node;
public StubComputeService(ComputeService delegate, NodeMetadata node) {
super(delegate);
this.node = checkNotNull(node, "node");
}
@Override
public void destroyNode(String id) {
// no-op
}
@Override
public Set<? extends NodeMetadata> createNodesInGroup(String group, int count) throws RunNodesException {
return ImmutableSet.of(node);
}
@Override
public Set<? extends NodeMetadata> createNodesInGroup(String group, int count, Template template) throws RunNodesException {
return ImmutableSet.of(node);
}
@Override
public Set<? extends NodeMetadata> createNodesInGroup(String group, int count, TemplateOptions templateOptions) throws RunNodesException {
return ImmutableSet.of(node);
}
}
}