package io.airlift.airship.cli;
import com.google.common.base.Charsets;
import com.google.common.base.Predicate;
import com.google.common.io.Files;
import io.airlift.airship.coordinator.Coordinator;
import io.airlift.airship.coordinator.ServiceInventory;
import io.airlift.airship.shared.AgentStatus;
import io.airlift.airship.shared.AgentStatusRepresentation;
import io.airlift.airship.shared.Assignment;
import io.airlift.airship.shared.CoordinatorStatus;
import io.airlift.airship.shared.CoordinatorStatusRepresentation;
import io.airlift.airship.shared.Repository;
import io.airlift.airship.shared.SlotLifecycleState;
import io.airlift.airship.shared.SlotStatus;
import io.airlift.airship.shared.SlotStatusRepresentation;
import io.airlift.airship.shared.UpgradeVersions;
import io.airlift.discovery.client.ServiceDescriptor;
import io.airlift.discovery.client.ServiceDescriptorsRepresentation;
import io.airlift.json.JsonCodec;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.transform;
import static io.airlift.airship.cli.CommanderResponse.createCommanderResponse;
import static io.airlift.airship.shared.AgentStatus.idGetter;
import static io.airlift.airship.shared.AgentStatusRepresentation.fromAgentStatus;
import static io.airlift.airship.shared.CoordinatorStatusRepresentation.fromCoordinatorStatus;
import static io.airlift.airship.shared.SlotStatus.uuidGetter;
import static io.airlift.airship.shared.SlotStatusRepresentation.fromSlotStatus;
import static io.airlift.airship.shared.VersionsUtil.checkAgentsVersion;
import static io.airlift.airship.shared.VersionsUtil.createAgentsVersion;
import static io.airlift.airship.shared.VersionsUtil.createSlotsVersion;
public class LocalCommander
implements Commander
{
private static final JsonCodec<ServiceDescriptorsRepresentation> SERVICE_DESCRIPTORS_CODEC = JsonCodec.jsonCodec(ServiceDescriptorsRepresentation.class);
private final String environment;
private final File localDirectory;
private final Coordinator coordinator;
private final Repository repository;
private final ServiceInventory serviceInventory;
public LocalCommander(String environment, File localDirectory, Coordinator coordinator, Repository repository, ServiceInventory serviceInventory)
{
this.environment = environment;
this.localDirectory = localDirectory;
this.coordinator = coordinator;
this.repository = repository;
this.serviceInventory = serviceInventory;
}
@Override
public CommanderResponse<List<SlotStatusRepresentation>> show(SlotFilter slotFilter)
{
List<SlotStatus> allSlotStatus = coordinator.getAllSlotStatus();
List<UUID> uuids = transform(allSlotStatus, SlotStatus.uuidGetter());
Predicate<SlotStatus> slotPredicate = slotFilter.toSlotPredicate(false, uuids);
List<SlotStatus> slots = coordinator.getAllSlotsStatus(slotPredicate);
// update just in case something changed
updateServiceInventory();
return createCommanderResponse(createSlotsVersion(slots), transform(slots, fromSlotStatus(coordinator.getAllSlotStatus(), repository)));
}
@Override
public List<SlotStatusRepresentation> install(AgentFilter agentFilter, int count, Assignment assignment, String expectedAgentsVersion)
{
// select the target agents
Predicate<AgentStatus> agentsPredicate = agentFilter.toAgentPredicate(
transform(coordinator.getAgents(), idGetter()),
transform(coordinator.getAllSlotStatus(), uuidGetter()),
true,
repository);
List<AgentStatus> agents = coordinator.getAgents(agentsPredicate);
// verify the expected status of agents
checkAgentsVersion(expectedAgentsVersion, agents);
// install the software
List<SlotStatus> slots = coordinator.install(agentsPredicate, count, assignment);
// update to latest state
updateServiceInventory();
// calculate unique prefix size with the new slots included
return transform(slots, fromSlotStatus(coordinator.getAllSlotStatus(), repository));
}
@Override
public List<SlotStatusRepresentation> upgrade(SlotFilter slotFilter, UpgradeVersions upgradeVersions, String expectedSlotsVersion, boolean force)
{
// build predicate
List<UUID> uuids = transform(coordinator.getAllSlotStatus(), SlotStatus.uuidGetter());
Predicate<SlotStatus> slotPredicate = slotFilter.toSlotPredicate(true, uuids);
// upgrade slots
List<SlotStatus> slots = coordinator.upgrade(slotPredicate, upgradeVersions, expectedSlotsVersion, force);
// update to latest state
updateServiceInventory();
// build results
return transform(slots, fromSlotStatus(coordinator.getAllSlotStatus(), repository));
}
@Override
public List<SlotStatusRepresentation> setState(SlotFilter slotFilter, SlotLifecycleState state, String expectedSlotsVersion)
{
// build predicate
List<UUID> uuids = transform(coordinator.getAllSlotStatus(), SlotStatus.uuidGetter());
Predicate<SlotStatus> slotPredicate = slotFilter.toSlotPredicate(true, uuids);
// before changing state (like starting) update just in case something changed
updateServiceInventory();
// set slots state
List<SlotStatus> slots = coordinator.setState(state, slotPredicate, expectedSlotsVersion);
// update to latest state
updateServiceInventory();
// build results
return transform(slots, fromSlotStatus(coordinator.getAllSlotStatus(), repository));
}
@Override
public List<SlotStatusRepresentation> terminate(SlotFilter slotFilter, String expectedSlotsVersion)
{
// build predicate
List<UUID> uuids = transform(coordinator.getAllSlotStatus(), SlotStatus.uuidGetter());
Predicate<SlotStatus> slotPredicate = slotFilter.toSlotPredicate(true, uuids);
// terminate slots
List<SlotStatus> slots = coordinator.terminate(slotPredicate, expectedSlotsVersion);
// update to latest state
updateServiceInventory();
// build results
return transform(slots, fromSlotStatus(coordinator.getAllSlotStatus(), repository));
}
@Override
public List<SlotStatusRepresentation> resetExpectedState(SlotFilter slotFilter, String expectedSlotsVersion)
{
// build predicate
List<UUID> uuids = transform(coordinator.getAllSlotStatus(), SlotStatus.uuidGetter());
Predicate<SlotStatus> slotPredicate = slotFilter.toSlotPredicate(true, uuids);
// rest slots expected state
List<SlotStatus> slots = coordinator.resetExpectedState(slotPredicate, expectedSlotsVersion);
// update just in case something changed
updateServiceInventory();
// build results
return transform(slots, fromSlotStatus(coordinator.getAllSlotStatus(), repository));
}
@Override
public boolean ssh(SlotFilter slotFilter, String command)
{
// build predicate
List<UUID> uuids = transform(coordinator.getAllSlotStatus(), SlotStatus.uuidGetter());
Predicate<SlotStatus> slotPredicate = slotFilter.toSlotPredicate(true, uuids);
// find the matching slots
List<SlotStatus> slots = newArrayList(coordinator.getAllSlotsStatus(slotPredicate));
// update just in case something changed
updateServiceInventory();
if (slots.isEmpty()) {
return false;
}
// execute the command against one of the slots
Collections.shuffle(slots);
Exec.execLocal(SlotStatusRepresentation.from(slots.get(0)), command);
return true;
}
@Override
public List<CoordinatorStatusRepresentation> showCoordinators(CoordinatorFilter coordinatorFilter)
{
Predicate<CoordinatorStatus> coordinatorPredicate = coordinatorFilter.toCoordinatorPredicate();
List<CoordinatorStatus> coordinatorStatuses = coordinator.getCoordinators(coordinatorPredicate);
// update just in case something changed
updateServiceInventory();
return transform(coordinatorStatuses, fromCoordinatorStatus(coordinator.getCoordinators()));
}
@Override
public List<CoordinatorStatusRepresentation> provisionCoordinators(String coordinatorConfigSpec,
int coordinatorCount,
String instanceType,
String availabilityZone,
String ami,
String keyPair,
String securityGroup,
String provisioningScriptsArtifact,
boolean waitForStartup)
{
throw new UnsupportedOperationException("Coordinators can not be provisioned in local mode");
}
@Override
public boolean sshCoordinator(CoordinatorFilter coordinatorFilter, String command)
{
throw new UnsupportedOperationException("Coordinator ssh no supported in local mode");
}
@Override
public CommanderResponse<List<AgentStatusRepresentation>> showAgents(AgentFilter agentFilter)
{
Predicate<AgentStatus> agentPredicate = agentFilter.toAgentPredicate(
transform(coordinator.getAgents(), idGetter()),
transform(coordinator.getAllSlotStatus(), SlotStatus.uuidGetter()),
true,
repository);
List<AgentStatus> agentStatuses = coordinator.getAgents(agentPredicate);
// update just in case something changed
updateServiceInventory();
return createCommanderResponse(createAgentsVersion(agentStatuses), transform(agentStatuses, fromAgentStatus(coordinator.getAgents(), repository)));
}
@Override
public List<AgentStatusRepresentation> provisionAgents(String agentConfig,
int agentCount,
String instanceType,
String availabilityZone,
String ami,
String keyPair,
String securityGroup,
String provisioningScriptsArtifact,
boolean waitForStartup)
{
throw new UnsupportedOperationException("Agents can not be provisioned in local mode");
}
@Override
public AgentStatusRepresentation terminateAgent(String agentId)
{
throw new UnsupportedOperationException("Agents can not be terminated in local mode");
}
@Override
public boolean sshAgent(AgentFilter agentFilter, String command)
{
throw new UnsupportedOperationException("Agent ssh no supported in local mode");
}
private void updateServiceInventory()
{
List<ServiceDescriptor> inventory = serviceInventory.getServiceInventory(coordinator.getAllSlotStatus());
ServiceDescriptorsRepresentation serviceDescriptors = new ServiceDescriptorsRepresentation(environment, inventory);
File serviceInventoryFile = new File(localDirectory, "service-inventory.json");
try {
Files.write(SERVICE_DESCRIPTORS_CODEC.toJson(serviceDescriptors), serviceInventoryFile, Charsets.UTF_8);
}
catch (IOException e) {
System.out.println("Unable to write " + serviceInventoryFile);
}
}
}