package io.fathom.cloud.compute.scheduler; import io.fathom.cloud.CloudException; import io.fathom.cloud.blobs.BlobData; import io.fathom.cloud.compute.actions.StartInstancesAction.StartInstanceData; import io.fathom.cloud.compute.services.ComputeDerivedMetadata; import io.fathom.cloud.compute.services.ComputeServices; import io.fathom.cloud.compute.services.SecurityGroups; import io.fathom.cloud.protobuf.CloudModel.InstanceData; import io.fathom.cloud.protobuf.CloudModel.InstanceState; import io.fathom.cloud.protobuf.CloudModel.NetworkAddressData; import io.fathom.cloud.protobuf.CloudModel.SecurityGroupData; import io.fathom.cloud.server.auth.Auth; import io.fathom.cloud.server.model.Project; import io.fathom.cloud.services.ImageKey; import io.fathom.cloud.services.ImageService; import java.io.IOException; import java.net.Inet6Address; import java.net.InetAddress; import java.util.List; import java.util.Set; import java.util.UUID; import javax.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Charsets; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.google.common.net.InetAddresses; public class StartInstanceOperation extends SchedulerOperation { private static final Logger log = LoggerFactory.getLogger(StartInstanceOperation.class); @Inject ImageService imageService; @Inject SecurityGroups securityGroups; @Inject ComputeServices computeServices; @Inject ComputeDerivedMetadata derivedMetadata; InstanceData instance; private Project project; private String token; private Auth auth; public void init(Auth auth, Project project, StartInstanceData start) { this.auth = auth; this.project = project; this.token = start.token; this.instance = start.instanceInfo; } @Override public boolean run() throws CloudException, IOException { List<SecurityGroupData> instanceSecurityGroups = securityGroups.getSecurityGroups(project, instance); SchedulerHost host = scheduler.findHost(instance.getHostId()); if (host == null) { throw new IllegalStateException(); } // Upload the image ImageService.Image image; { image = imageService.findImage(project, instance.getImageId()); if (image == null) { throw new IllegalStateException("Cannot find image: " + instance.getImageId()); } ImageKey imageKey = image.getUniqueKey(); if (!host.hasImage(imageKey)) { // TODO: Support side-load BlobData imageData = imageService.getImageBlob(image); host.uploadImage(imageKey, imageData); } } instance = services.assignIps(project, host, instance); UUID containerId = host.createContainer(instance, image); services.updateInstance(instance, InstanceData.newBuilder().setHostCookie(containerId.toString())); updateSecurityGroupIpsets(); try (ConfigurationOperation config = host.startConfiguration()) { config.configureFirewall(instance, instanceSecurityGroups); config.applyChanges(); } host.setSecret(containerId, SchedulerHost.SECRET_TOKEN, token.getBytes(Charsets.UTF_8)); host.startContainer(containerId); instance = services.updateInstance(instance, InstanceData.newBuilder().setInstanceState(InstanceState.RUNNING)); derivedMetadata.instanceUpdated(project, instance); return true; } private void updateSecurityGroupIpsets() throws CloudException, IOException { Set<Long> launchSgs = Sets.newHashSet(instance.getSecurityGroupIdList()); Multimap<Long, InstanceData> sgInstances = HashMultimap.create(); for (InstanceData peer : computeServices.listInstances(auth, project)) { boolean valid = true; switch (peer.getInstanceState()) { case STOPPED: case TERMINATED: valid = false; break; } if (!valid) { continue; } for (Long peerSg : peer.getSecurityGroupIdList()) { if (!launchSgs.contains(peerSg)) { continue; } sgInstances.put(peerSg, peer); } } for (Long instanceSg : instance.getSecurityGroupIdList()) { sgInstances.put(instanceSg, instance); } Set<Long> doneHostIds = Sets.newHashSet(); for (InstanceData instance : sgInstances.values()) { long hostId = instance.getHostId(); if (doneHostIds.contains(hostId)) { continue; } SchedulerHost host = scheduler.findHost(hostId); if (host == null) { log.error("Unable to find host: " + hostId); continue; } try (ConfigurationOperation config = host.startConfiguration()) { for (long sg : sgInstances.keySet()) { Set<String> ips = Sets.newHashSet(); for (InstanceData i : sgInstances.get(sg)) { for (NetworkAddressData nai : i.getNetwork().getAddressesList()) { InetAddress address = InetAddresses.forString(nai.getIp()); if (address instanceof Inet6Address) { ips.add(InetAddresses.toAddrString(address)); } } } config.configureIpset(sg, ips); } config.applyChanges(); } doneHostIds.add(hostId); } } }