package io.airlift.airship.cli;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.net.InetAddresses;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import io.airlift.airship.agent.Agent;
import io.airlift.airship.agent.DeploymentManagerFactory;
import io.airlift.airship.agent.DirectoryDeploymentManagerFactory;
import io.airlift.airship.agent.LauncherLifecycleManager;
import io.airlift.airship.agent.LifecycleManager;
import io.airlift.airship.agent.Slot;
import io.airlift.airship.coordinator.Coordinator;
import io.airlift.airship.coordinator.CoordinatorConfig;
import io.airlift.airship.coordinator.HttpRepository;
import io.airlift.airship.coordinator.HttpServiceInventory;
import io.airlift.airship.coordinator.InMemoryStateManager;
import io.airlift.airship.coordinator.Instance;
import io.airlift.airship.coordinator.MavenRepository;
import io.airlift.airship.coordinator.Provisioner;
import io.airlift.airship.coordinator.RemoteAgent;
import io.airlift.airship.coordinator.RemoteAgentFactory;
import io.airlift.airship.coordinator.RemoteCoordinator;
import io.airlift.airship.coordinator.RemoteCoordinatorFactory;
import io.airlift.airship.coordinator.RemoteSlot;
import io.airlift.airship.coordinator.ServiceInventory;
import io.airlift.airship.coordinator.StateManager;
import io.airlift.airship.shared.AgentLifecycleState;
import io.airlift.airship.shared.AgentStatus;
import io.airlift.airship.shared.CoordinatorLifecycleState;
import io.airlift.airship.shared.CoordinatorStatus;
import io.airlift.airship.shared.ExpectedSlotStatus;
import io.airlift.airship.shared.Installation;
import io.airlift.airship.shared.Repository;
import io.airlift.airship.shared.RepositorySet;
import io.airlift.airship.shared.SlotStatus;
import io.airlift.discovery.client.ServiceDescriptor;
import io.airlift.json.JsonCodec;
import io.airlift.units.Duration;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URI;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import static com.google.common.collect.Lists.newArrayList;
public class CommanderFactory
{
private static final URI FAKE_LOCAL_URI = URI.create("java://localhost");
private static final Duration COMMAND_TIMEOUT = new Duration(5, TimeUnit.MINUTES);
private String environment;
private URI coordinatorUri;
private final List<String> repositories = newArrayList();
private final List<String> mavenDefaultGroupIds = newArrayList();
private String coordinatorId = "local";
private String agentId = "local";
private String location;
private String instanceType = "local";
private InetAddress internalIp;
private String externalAddress;
private boolean useInternalAddress;
private boolean allowDuplicateInstallations;
public CommanderFactory setEnvironment(String environment)
{
this.environment = environment;
return this;
}
public CommanderFactory setCoordinatorUri(URI coordinatorUri)
{
this.coordinatorUri = coordinatorUri;
return this;
}
public CommanderFactory setRepositories(List<String> repositories)
{
this.repositories.clear();
this.repositories.addAll(repositories);
return this;
}
public CommanderFactory setMavenDefaultGroupIds(List<String> mavenDefaultGroupIds)
{
this.mavenDefaultGroupIds.clear();
this.mavenDefaultGroupIds.addAll(mavenDefaultGroupIds);
return this;
}
public void setCoordinatorId(String coordinatorId)
{
Preconditions.checkNotNull(coordinatorId, "coordinatorId is null");
Preconditions.checkArgument(!coordinatorId.isEmpty(), "coordinatorId is empty");
this.coordinatorId = coordinatorId;
}
public void setAgentId(String agentId)
{
Preconditions.checkNotNull(agentId, "agentId is null");
Preconditions.checkArgument(!agentId.isEmpty(), "agentId is empty");
this.agentId = agentId;
}
public void setLocation(String location)
{
Preconditions.checkNotNull(location, "location is null");
Preconditions.checkArgument(!location.isEmpty(), "location is empty");
this.location = location;
}
public void setInstanceType(String instanceType)
{
Preconditions.checkNotNull(instanceType, "instanceType is null");
Preconditions.checkArgument(!instanceType.isEmpty(), "instanceType is empty");
this.instanceType = instanceType;
}
public void setInternalIp(String internalIp)
{
Preconditions.checkNotNull(internalIp, "internalIp is null");
Preconditions.checkArgument(!internalIp.isEmpty(), "internalIp is empty");
this.internalIp = InetAddresses.forString(internalIp);
}
public void setExternalAddress(String externalAddress)
{
Preconditions.checkNotNull(externalAddress, "externalAddress is null");
Preconditions.checkArgument(!externalAddress.isEmpty(), "externalAddress is empty");
this.externalAddress = externalAddress;
}
public void setAllowDuplicateInstallations(boolean allowDuplicateInstallations)
{
this.allowDuplicateInstallations = allowDuplicateInstallations;
}
public void setUseInternalAddress(boolean useInternalAddress)
{
this.useInternalAddress = useInternalAddress;
}
public Commander build()
throws IOException
{
Preconditions.checkNotNull(coordinatorUri, "coordinatorUri is null");
String scheme = coordinatorUri.getScheme();
if ("http".equals(scheme)) {
return new HttpCommander(coordinatorUri, useInternalAddress);
}
else if ("file".equals(scheme) || scheme == null) {
return createLocalCommander();
}
throw new IllegalAccessError("Unsupported coordinator protocol " + scheme);
}
private LocalCommander createLocalCommander()
{
Preconditions.checkNotNull(coordinatorUri, "coordinatorUri is null");
Preconditions.checkNotNull(environment, "environment is null");
Preconditions.checkNotNull(this.repositories, "binaryRepositories is null");
//
// Create agent
//
String slotsDir = coordinatorUri.getPath();
String agentLocation = this.location == null ? Joiner.on('/').join("", "local", agentId, "agent") : location;
DeploymentManagerFactory deploymentManagerFactory = new DirectoryDeploymentManagerFactory(agentLocation, slotsDir, COMMAND_TIMEOUT);
LifecycleManager lifecycleManager = new LauncherLifecycleManager(
environment,
internalIp,
externalAddress,
null,
COMMAND_TIMEOUT,
COMMAND_TIMEOUT,
new File(slotsDir, "service-inventory.json").toURI());
Agent agent = new Agent(agentId,
agentLocation,
slotsDir,
FAKE_LOCAL_URI,
FAKE_LOCAL_URI,
null,
deploymentManagerFactory,
lifecycleManager,
COMMAND_TIMEOUT);
//
// Create coordinator
//
CoordinatorConfig coordinatorConfig = new CoordinatorConfig()
.setRepositories(repositories)
.setDefaultRepositoryGroupId(mavenDefaultGroupIds);
Repository repository = new RepositorySet(ImmutableSet.<Repository>of(
new MavenRepository(coordinatorConfig),
new HttpRepository(coordinatorConfig)));
ServiceInventory serviceInventory = new HttpServiceInventory(repository,
JsonCodec.listJsonCodec(ServiceDescriptor.class),
new File(slotsDir, "service-inventory-cache"));
Provisioner provisioner = new LocalProvisioner();
StateManager stateManager = new InMemoryStateManager();
for (SlotStatus slotStatus : agent.getAgentStatus().getSlotStatuses()) {
stateManager.setExpectedState(new ExpectedSlotStatus(slotStatus.getId(), slotStatus.getState(), slotStatus.getAssignment()));
}
RemoteCoordinatorFactory remoteCoordinatorFactory = new LocalRemoteCoordinatorFactory();
RemoteAgentFactory remoteAgentFactory = new LocalRemoteAgentFactory(agent);
String coordinatorLocation = this.location == null ? Joiner.on('/').join("", "local", coordinatorId, "coordinator") : location;
CoordinatorStatus coordinatorStatus = new CoordinatorStatus(coordinatorId,
CoordinatorLifecycleState.ONLINE,
coordinatorId,
FAKE_LOCAL_URI,
FAKE_LOCAL_URI,
coordinatorLocation,
instanceType);
Coordinator coordinator = new Coordinator(coordinatorStatus,
remoteCoordinatorFactory,
remoteAgentFactory,
repository,
provisioner,
stateManager,
serviceInventory,
new Duration(100, TimeUnit.DAYS),
allowDuplicateInstallations);
return new LocalCommander(environment, new File(slotsDir), coordinator, repository, serviceInventory);
}
private class LocalProvisioner
implements Provisioner
{
@Override
public List<Instance> listCoordinators()
{
String coordinatorLocation = location == null ? Joiner.on('/').join("", "local", coordinatorId, "coordinator") : location;
return ImmutableList.of(new Instance(coordinatorId, instanceType, coordinatorLocation, FAKE_LOCAL_URI, FAKE_LOCAL_URI));
}
@Override
public List<Instance> provisionCoordinators(String coordinatorConfigSpec,
int coordinatorCount,
String instanceType,
String availabilityZone,
String ami,
String keyPair,
String securityGroup,
String provisioningScriptsArtifact)
{
throw new UnsupportedOperationException("Coordinators can not be provisioned in local mode");
}
@Override
public List<Instance> listAgents()
{
String agentLocation = location == null ? Joiner.on('/').join("", "local", agentId, "agent") : location;
return ImmutableList.of(new Instance(agentId, instanceType, agentLocation, FAKE_LOCAL_URI, FAKE_LOCAL_URI));
}
@Override
public List<Instance> provisionAgents(String agentConfig,
int agentCount,
String instanceType,
String availabilityZone,
String ami,
String keyPair,
String securityGroup,
String provisioningScriptsArtifact)
{
throw new UnsupportedOperationException("Agents can not be provisioned in local mode");
}
@Override
public void terminateAgents(Iterable<String> instanceIds)
{
throw new UnsupportedOperationException("Agents can not be terminated in local mode");
}
}
private class LocalRemoteCoordinatorFactory
implements RemoteCoordinatorFactory
{
@Override
public RemoteCoordinator createRemoteCoordinator(Instance instance, CoordinatorLifecycleState state)
{
throw new UnsupportedOperationException("Coordinators can not be provisioned in local mode");
}
}
private class LocalRemoteAgentFactory
implements RemoteAgentFactory
{
private final Agent agent;
public LocalRemoteAgentFactory(Agent agent)
{
this.agent = agent;
}
@Override
public RemoteAgent createRemoteAgent(Instance instance, AgentLifecycleState state)
{
Preconditions.checkNotNull(instance, "instance is null");
return new LocalRemoteAgent(agent);
}
}
private class LocalRemoteAgent
implements RemoteAgent
{
private final Agent agent;
public LocalRemoteAgent(Agent agent)
{
this.agent = agent;
}
@Override
public void setInternalUri(URI uri)
{
Preconditions.checkArgument(FAKE_LOCAL_URI.equals(uri), "uri is not '" + FAKE_LOCAL_URI + "'");
}
@Override
public SlotStatus install(Installation installation)
{
return agent.install(installation).changeInstanceId(agentId);
}
@Override
public AgentStatus status()
{
AgentStatus agentStatus = agent.getAgentStatus();
return new AgentStatus(agentStatus.getAgentId(),
agentStatus.getState(),
agentId,
agentStatus.getInternalUri(),
agentStatus.getExternalUri(),
agentStatus.getLocation(),
instanceType,
agentStatus.getSlotStatuses(),
agentStatus.getResources());
}
@Override
public List<? extends RemoteSlot> getSlots()
{
ImmutableList.Builder<RemoteSlot> builder = ImmutableList.builder();
for (Slot slot : agent.getAllSlots()) {
builder.add(new LocalRemoteSlot(slot, agentId));
}
return builder.build();
}
@Override
public ListenableFuture<?> updateStatus()
{
return Futures.immediateFuture(null);
}
@Override
public void setServiceInventory(List<ServiceDescriptor> serviceInventory)
{
}
}
private static class LocalRemoteSlot
implements RemoteSlot
{
private final Slot slot;
private final String instanceId;
public LocalRemoteSlot(Slot slot, String instanceId)
{
this.slot = slot;
this.instanceId = instanceId;
}
@Override
public UUID getId()
{
return slot.getId();
}
@Override
public SlotStatus terminate()
{
return slot.terminate().changeInstanceId(instanceId);
}
@Override
public SlotStatus assign(Installation installation)
{
return slot.assign(installation).changeInstanceId(instanceId);
}
@Override
public SlotStatus status()
{
return slot.status().changeInstanceId(instanceId);
}
@Override
public SlotStatus start()
{
return slot.start().changeInstanceId(instanceId);
}
@Override
public SlotStatus restart()
{
return slot.restart().changeInstanceId(instanceId);
}
@Override
public SlotStatus stop()
{
return slot.stop().changeInstanceId(instanceId);
}
@Override
public SlotStatus kill()
{
return slot.kill().changeInstanceId(instanceId);
}
}
public static class ToUriFunction
implements Function<String, URI>
{
public URI apply(String uri)
{
if (!uri.endsWith("/")) {
uri = uri + "/";
}
return URI.create(uri);
}
}
}