/*
* 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.software.base.test.mysql;
import java.io.File;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntityInitializer;
import org.apache.brooklyn.api.entity.EntityLocal;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.location.MachineLocation;
import org.apache.brooklyn.api.location.OsDetails;
import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
import org.apache.brooklyn.core.entity.Attributes;
import org.apache.brooklyn.core.location.BasicOsDetails.OsVersions;
import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks;
import org.apache.brooklyn.entity.stock.BasicStartable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation.LocalhostMachine;
import org.apache.brooklyn.location.ssh.SshMachineLocation;
import org.apache.brooklyn.util.core.task.DynamicTasks;
import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.core.task.ssh.SshTasks;
import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
import org.apache.brooklyn.util.ssh.BashCommands;
import org.apache.brooklyn.util.text.ComparableVersion;
import org.apache.brooklyn.util.time.Duration;
import org.apache.brooklyn.util.time.Time;
import com.google.common.base.Predicates;
import com.google.common.base.Splitter;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.Iterables;
public class DynamicToyMySqlEntityBuilder {
private static final Logger log = LoggerFactory.getLogger(DynamicToyMySqlEntityBuilder.class);
public static EntitySpec<? extends Entity> spec() {
return EntitySpec.create(BasicStartable.class).addInitializer(MySqlEntityInitializer.class);
}
public static final String downloadUrl(Entity e, boolean isLocalhost) {
if (isLocalhost) {
for (int i=50; i>20; i--) {
String f = System.getProperty("user.home")+"/.brooklyn/repository/MySqlNode/5.5."+i+"/mysql-5.5."+i+"-osx10.6-x86_64.tar.gz";
if (new File(f).exists())
return "file://"+f;
}
}
// download
String version = "5.5.37";
String osTag = getOsTag(e);
return "http://cdn.mysql.com/archives/mysql-5.5/mysql-"+version+"-"+osTag+".tar.gz";
}
public static final String installDir(Entity e, boolean isLocalhost) {
String url = downloadUrl(e, isLocalhost);
String archive = Iterables.find(Splitter.on('/').omitEmptyStrings().split(url), Predicates.containsPattern(".tar.gz"));
return archive.replace(".tar.gz", "");
}
public static final String dir(Entity e) {
return "/tmp/brooklyn-mysql-"+e.getId();
}
// copied from MySqlSshDriver
public static String getOsTag(Entity e) {
// e.g. "osx10.6-x86_64"; see http://www.mysql.com/downloads/mysql/#downloads
OsDetails os = ((SshMachineLocation)Iterables.getOnlyElement(e.getLocations())).getOsDetails();
if (os == null) return "linux2.6-x86_64";
if (os.isMac()) {
if (!os.is64bit()) {
throw new IllegalStateException("Only 64 bit MySQL build is available for OS X");
}
return "osx10.6-x86_64";
}
//assume generic linux
String osp1 = "linux2.6";
String osp2 = os.is64bit() ? "x86_64" : "i686";
return osp1+"-"+osp2;
}
public static class MySqlEntityInitializer implements EntityInitializer {
public void apply(final EntityLocal entity) {
new MachineLifecycleEffectorTasks() {
@Override
protected String startProcessesAtMachine(Supplier<MachineLocation> machineS) {
DynamicTasks.queue(
SshEffectorTasks.ssh(
"mkdir "+dir(entity),
"cd "+dir(entity),
BashCommands.downloadToStdout(downloadUrl(entity, isLocalhost(machineS)))+" | tar xvz"
).summary("download mysql").returning(SshTasks.returningStdoutLoggingInfo(log, true)));
if (isLinux(machineS)) {
DynamicTasks.queue(SshEffectorTasks.ssh(BashCommands.installPackage("libaio1")));
}
DynamicTasks.queue(
SshEffectorTasks.put(".my.cnf")
.contents(String.format("[mysqld]\nbasedir=%s/%s\n", dir(entity), installDir(entity, isLocalhost(machineS)))),
SshEffectorTasks.ssh(
"cd "+dir(entity)+"/*",
"./scripts/mysql_install_db",
"./support-files/mysql.server start > out.log 2> err.log < /dev/null"
).summary("setup and run mysql").returning(SshTasks.returningStdoutLoggingInfo(log, true)));
return "submitted start";
}
protected void postStartCustom() {
// if it's still up after 5s assume we are good
Time.sleep(Duration.FIVE_SECONDS);
if (!DynamicTasks.queue(SshEffectorTasks.isPidFromFileRunning(dir(entity)+"/*/data/*.pid")).get()) {
// but if it's not up add a bunch of other info
log.warn("MySQL did not start: "+dir(entity));
ProcessTaskWrapper<Integer> info = DynamicTasks.queue(SshEffectorTasks.ssh(
"cd "+dir(entity)+"/*",
"cat out.log",
"cat err.log > /dev/stderr")).block();
log.info("STDOUT:\n"+info.getStdout());
log.info("STDERR:\n"+info.getStderr());
BrooklynTaskTags.addTagsDynamically(Tasks.current(),
BrooklynTaskTags.tagForStream("console (nohup stdout)", Suppliers.ofInstance(info.getStdout()), null),
BrooklynTaskTags.tagForStream("console (nohup stderr)", Suppliers.ofInstance(info.getStderr()), null));
throw new IllegalStateException("MySQL appears not to be running");
}
// and set the PID
entity().sensors().set(Attributes.PID,
Integer.parseInt(DynamicTasks.queue(SshEffectorTasks.ssh("cat "+dir(entity)+"/*/data/*.pid")).block().getStdout().trim()));
// TODO Without this, tests fail because nothing else sets serviceUp!
// Really should set this with a Feed that checks pid periodically.
// Should this instead be using SERVICE_NOT_UP_INDICATORS?
entity().sensors().set(Attributes.SERVICE_UP, true);
}
@Override
protected String stopProcessesAtMachine() {
// TODO Where is best place to set?
// Really should set this with a Feed that checks pid periodically.
entity().sensors().set(Attributes.SERVICE_UP, false);
Integer pid = entity().getAttribute(Attributes.PID);
if (pid==null) {
log.info("mysql not running");
return "No pid -- is it running?";
}
DynamicTasks.queue(SshEffectorTasks.ssh(
"cd "+dir(entity)+"/*",
"./support-files/mysql.server stop"
).summary("stop mysql"));
return "submitted stop";
}
}.attachLifecycleEffectors(entity);
}
}
private static boolean isLocalhost(Supplier<MachineLocation> machineS) {
return machineS.get() instanceof LocalhostMachine;
}
private static boolean isLinux(Supplier<MachineLocation> machineS) {
return machineS.get().getMachineDetails().getOsDetails().isLinux();
}
}