Java tutorial
/* * 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.jclouds.vagrant.compute; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.Resource; import javax.inject.Inject; import javax.inject.Named; import org.jclouds.compute.ComputeServiceAdapter; import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata.Status; 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.Volume.Type; import org.jclouds.compute.util.AutomaticHardwareIdSpec; import org.jclouds.domain.Location; import org.jclouds.domain.LocationBuilder; import org.jclouds.domain.LocationScope; import org.jclouds.domain.LoginCredentials; import org.jclouds.location.suppliers.all.JustProvider; import org.jclouds.logging.Logger; import org.jclouds.vagrant.api.VagrantApiFacade; import org.jclouds.vagrant.domain.VagrantNode; import org.jclouds.vagrant.internal.MachineConfig; import org.jclouds.vagrant.internal.VagrantNodeRegistry; import org.jclouds.vagrant.reference.VagrantConstants; import org.jclouds.vagrant.util.VagrantUtils; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; public class VagrantComputeServiceAdapter implements ComputeServiceAdapter<VagrantNode, Hardware, Image, Location> { private static final Pattern PATTERN_IP_ADDR = Pattern.compile("inet ([0-9\\.]+)/(\\d+)"); private static final Pattern PATTERN_IPCONFIG = Pattern.compile("IPv4 Address[ .]+: ([0-9\\.]+)"); @Resource protected Logger logger = Logger.NULL; private final File home; private final JustProvider locationSupplier; private final VagrantNodeRegistry nodeRegistry; private final MachineConfig.Factory machineConfigFactory; private final VagrantApiFacade.Factory cliFactory; private final Supplier<? extends Map<String, Hardware>> hardwareSupplier; private final Supplier<Collection<Image>> imageListSupplier; private final Function<String, Image> imageIdToImage; @Inject VagrantComputeServiceAdapter(@Named(VagrantConstants.JCLOUDS_VAGRANT_HOME) String home, JustProvider locationSupplier, VagrantNodeRegistry nodeRegistry, MachineConfig.Factory machineConfigFactory, VagrantApiFacade.Factory cliFactory, Supplier<? extends Map<String, Hardware>> hardwareSupplier, Supplier<Collection<Image>> imageListSupplier, Function<String, Image> imageIdToImage) { this.home = new File(home); this.locationSupplier = locationSupplier; this.nodeRegistry = nodeRegistry; this.machineConfigFactory = machineConfigFactory; this.cliFactory = cliFactory; this.hardwareSupplier = hardwareSupplier; this.imageListSupplier = imageListSupplier; this.imageIdToImage = imageIdToImage; this.home.mkdirs(); } @Override public NodeAndInitialCredentials<VagrantNode> createNodeWithGroupEncodedIntoName(String group, String name, Template template) { String machineName = removeFromStart(name, group); File nodePath = new File(home, group); init(nodePath, machineName, template); NodeAndInitialCredentials<VagrantNode> node = startMachine(nodePath, group, machineName, template.getImage(), template.getHardware()); nodeRegistry.add(node.getNode()); return node; } private NodeAndInitialCredentials<VagrantNode> startMachine(File path, String group, String name, Image image, Hardware hardware) { String provider = image.getUserMetadata().get(VagrantConstants.USER_META_PROVIDER); VagrantApiFacade vagrant = cliFactory.create(path); String rawOutput = vagrant.up(name, provider); String output = normalizeOutput(name, rawOutput); OsFamily osFamily = image.getOperatingSystem().getFamily(); String id = group + "/" + name; VagrantNode node = VagrantNode.builder().setPath(path).setId(id).setGroup(group).setName(name) .setImage(image).setHardware(hardware) .setNetworks(getNetworks(output, getOsInterfacePattern(osFamily))).setHostname(getHostname(output)) .build(); node.setMachineState(Status.RUNNING); LoginCredentials loginCredentials = null; if (osFamily != OsFamily.WINDOWS) { loginCredentials = vagrant.sshConfig(name); } // PrioritizeCredentialsFromTemplate will overwrite loginCredentials with image credentials // AdaptingComputeServiceStrategies saves the merged credentials in credentialStore return new NodeAndInitialCredentials<VagrantNode>(node, node.id(), loginCredentials); } private String normalizeOutput(String name, String output) { return output.replaceAll("(?m)^([^,]*,){4}", "").replace("==> " + name + ": ", "") // Vagrant shows some of the \n verbatim in provisioning command results. .replace("\\n", "\n"); } private Pattern getOsInterfacePattern(OsFamily osFamily) { if (osFamily == OsFamily.WINDOWS) { return PATTERN_IPCONFIG; } else { return PATTERN_IP_ADDR; } } private Collection<String> getNetworks(String output, Pattern ifPattern) { String networks = getDelimitedString(output, VagrantConstants.DELIMITER_NETWORKS_START, VagrantConstants.DELIMITER_NETWORKS_END); Matcher m = ifPattern.matcher(networks); Collection<String> ips = new ArrayList<String>(); while (m.find()) { String network = m.group(1); ips.add(network); } return ips; } private String getHostname(String output) { return getDelimitedString(output, VagrantConstants.DELIMITER_HOSTNAME_START, VagrantConstants.DELIMITER_HOSTNAME_END); } private String getDelimitedString(String value, String delimStart, String delimEnd) { int startPos = value.indexOf(delimStart); int endPos = value.indexOf(delimEnd); if (startPos == -1) { throw new IllegalStateException("Delimiter " + delimStart + " not found in output \n" + value); } if (endPos == -1) { throw new IllegalStateException("Delimiter " + delimEnd + " not found in output \n" + value); } return value.substring(startPos + delimStart.length(), endPos).trim(); } private void init(File path, String name, Template template) { try { writeVagrantfile(path); initMachineConfig(path, name, template); } catch (IOException e) { throw new IllegalStateException( "Unable to initialize Vagrant configuration at " + path + " for machine " + name, e); } } private void writeVagrantfile(File path) throws IOException { path.mkdirs(); VagrantUtils.write(new File(path, VagrantConstants.VAGRANTFILE), getClass().getClassLoader().getResourceAsStream(VagrantConstants.VAGRANTFILE)); } private void initMachineConfig(File path, String name, Template template) { MachineConfig config = machineConfigFactory.newInstance(path, name); List<? extends Volume> volumes = template.getHardware().getVolumes(); if (volumes != null) { if (volumes.size() == 1) { Volume volume = Iterables.getOnlyElement(volumes); if (volume.getType() != Type.LOCAL || volume.getSize() != null) { throw new IllegalStateException( "Custom volume settings not supported. Volumes required: " + volumes); } } else if (volumes.size() > 1) { throw new IllegalStateException( "Custom volume settings not supported. Volumes required: " + volumes); } } config.save(ImmutableMap.<String, Object>of(VagrantConstants.CONFIG_BOX, template.getImage().getName(), VagrantConstants.CONFIG_OS_FAMILY, template.getImage().getOperatingSystem().getFamily(), VagrantConstants.CONFIG_HARDWARE_ID, getHardwareId(template), VagrantConstants.CONFIG_MEMORY, Integer.toString(template.getHardware().getRam()), VagrantConstants.CONFIG_CPUS, Integer.toString(countProcessors(template)))); } private String getHardwareId(Template template) { String id = template.getHardware().getId(); if (AutomaticHardwareIdSpec.isAutomaticId(id)) { return VagrantConstants.MACHINES_AUTO_HARDWARE; } else { return id; } } private int countProcessors(Template template) { int cnt = 0; for (Processor p : template.getHardware().getProcessors()) { cnt += p.getCores(); } return cnt; } @Override public Iterable<Hardware> listHardwareProfiles() { return hardwareSupplier.get().values(); } @Override public Iterable<Image> listImages() { return imageListSupplier.get(); } @Override public Image getImage(String id) { return imageIdToImage.apply(id); } @Override public Iterable<Location> listLocations() { Location provider = Iterables.getOnlyElement(locationSupplier.get()); return ImmutableList.of(new LocationBuilder().id("localhost").description("localhost").parent(provider) .scope(LocationScope.HOST).build()); } @Override public VagrantNode getNode(String id) { // needed for BaseComputeServiceLiveTest.testAScriptExecutionAfterBootWithBasicTemplate() // waits for the thread updating the credentialStore to execute try { Thread.sleep(200); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw Throwables.propagate(e); } return nodeRegistry.get(id); } @Override public void destroyNode(String id) { VagrantNode node = nodeRegistry.get(id); node.setMachineState(Status.TERMINATED); getMachine(node).destroy(node.name()); nodeRegistry.onTerminated(node); deleteMachine(node); } private void deleteMachine(VagrantNode node) { File nodeFolder = node.path(); File machinesFolder = new File(nodeFolder, VagrantConstants.MACHINES_CONFIG_SUBFOLDER); String filePattern = node.name() + "."; logger.debug("Deleting machine %s", node.id()); VagrantUtils.deleteFiles(machinesFolder, filePattern); // No more machines in this group, remove everything if (machinesFolder.list().length == 0) { logger.debug("Machine %s is last in group, deleting Vagrant folder %s", node.id(), nodeFolder.getAbsolutePath()); VagrantUtils.deleteFolder(nodeFolder); } } @Override public void rebootNode(String id) { halt(id); VagrantNode node = nodeRegistry.get(id); String provider = node.image().getUserMetadata().get(VagrantConstants.USER_META_PROVIDER); String name = node.name(); VagrantApiFacade vagrant = getMachine(node); vagrant.up(name, provider); node.setMachineState(Status.RUNNING); } private void halt(String id) { VagrantNode node = nodeRegistry.get(id); String name = node.name(); VagrantApiFacade vagrant = getMachine(node); try { vagrant.halt(name); node.setMachineState(Status.SUSPENDED); } catch (IllegalStateException e) { logger.warn(e, "Failed graceful shutdown of machine " + id + ". Will try to halt it forcefully instead."); vagrant.haltForced(name); } } @Override public void resumeNode(String id) { VagrantNode node = nodeRegistry.get(id); String provider = node.image().getUserMetadata().get(VagrantConstants.USER_META_PROVIDER); String name = node.name(); VagrantApiFacade vagrant = getMachine(node); vagrant.up(name, provider); node.setMachineState(Status.RUNNING); } @Override public void suspendNode(String id) { halt(id); VagrantNode node = nodeRegistry.get(id); node.setMachineState(Status.SUSPENDED); } @Override public Iterable<VagrantNode> listNodes() { return nodeRegistry.list(); } @Override public Iterable<VagrantNode> listNodesByIds(final Iterable<String> ids) { return Iterables.filter(listNodes(), new Predicate<VagrantNode>() { @Override public boolean apply(VagrantNode input) { return Iterables.contains(ids, input.id()); } }); } private VagrantApiFacade getMachine(VagrantNode node) { File nodePath = node.path(); return cliFactory.create(nodePath); } private String removeFromStart(String name, String group) { if (name.startsWith(group)) { String machineName = name.substring(group.length()); // Can't pass names starting with dash on the command line if (machineName.startsWith("-")) { return machineName.substring(1); } else { return machineName; } } else { return name; } } }