package io.cattle.platform.process.instance; import static io.cattle.platform.core.constants.InstanceConstants.FIELD_LABELS; import static io.cattle.platform.core.constants.InstanceConstants.VOLUME_CLEANUP_STRATEGY_ALL; import static io.cattle.platform.core.constants.InstanceConstants.VOLUME_CLEANUP_STRATEGY_NONE; import static io.cattle.platform.core.constants.InstanceConstants.VOLUME_CLEANUP_STRATEGY_UNNAMED; import static io.cattle.platform.core.model.tables.MountTable.*; import io.cattle.platform.core.constants.CommonStatesConstants; import io.cattle.platform.core.constants.InstanceLinkConstants; import io.cattle.platform.core.constants.VolumeConstants; import io.cattle.platform.core.dao.GenericMapDao; import io.cattle.platform.core.dao.VolumeDao; import io.cattle.platform.core.model.Instance; import io.cattle.platform.core.model.InstanceLink; import io.cattle.platform.core.model.Mount; import io.cattle.platform.core.model.Nic; import io.cattle.platform.core.model.Port; import io.cattle.platform.core.model.Volume; import io.cattle.platform.core.util.SystemLabels; import io.cattle.platform.engine.handler.HandlerResult; import io.cattle.platform.engine.process.ProcessInstance; import io.cattle.platform.engine.process.ProcessState; import io.cattle.platform.object.process.StandardProcess; import io.cattle.platform.object.util.DataAccessor; import io.cattle.platform.process.base.AbstractDefaultProcessHandler; import io.cattle.platform.process.common.util.ProcessUtils; import io.cattle.platform.process.mount.MountDeactivate; import io.github.ibuildthecloud.gdapi.condition.Condition; import io.github.ibuildthecloud.gdapi.condition.ConditionType; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.inject.Inject; import javax.inject.Named; import org.apache.commons.lang3.StringUtils; @Named public class InstanceRemove extends AbstractDefaultProcessHandler { InstanceStop instanceStop; GenericMapDao mapDao; @Inject VolumeDao volumeDao; @Override public HandlerResult handle(ProcessState state, ProcessInstance process) { final Instance instance = (Instance)state.getResource(); Map<String, Object> result = new HashMap<String, Object>(); network(instance, state.getData()); storage(instance, state.getData()); for (Port port : getObjectManager().children(instance, Port.class)) { deactivateThenRemove(port, state.getData()); } for (InstanceLink link : getObjectManager().children(instance, InstanceLink.class, InstanceLinkConstants.FIELD_INSTANCE_ID)) { deactivateThenRemove(link, state.getData()); } for (InstanceLink link : getObjectManager().children(instance, InstanceLink.class, InstanceLinkConstants.FIELD_TARGET_INSTANCE_ID)) { objectManager.setFields(link, InstanceLinkConstants.FIELD_TARGET_INSTANCE_ID, (Object)null); } deleteVolumes(instance, state); deallocate(instance, null); objectManager.reload(instance); return new HandlerResult(result); } protected void storage(Instance instance, Map<String, Object> data) { List<Volume> volumes = getObjectManager().children(instance, Volume.class); for (Volume volume : volumes) { if (volume.getDeviceNumber() == 0) { remove(volume, data); } else { execute("volume.detach", volume, null); } } Map<Object, Object> criteria = new HashMap<Object, Object>(); criteria.put(MOUNT.REMOVED, new Condition(ConditionType.NULL)); criteria.put(MOUNT.STATE, new Condition(ConditionType.NOTIN, MountDeactivate.MOUNT_STATES)); criteria.put(MOUNT.INSTANCE_ID, instance.getId()); List<Mount> mounts = getObjectManager().find(Mount.class, criteria); for (Mount mount : mounts) { objectProcessManager.scheduleStandardProcess(StandardProcess.DEACTIVATE, mount, data); } } private void deleteVolumes(Instance instance, ProcessState state) { Object b = DataAccessor.fieldMap(instance, FIELD_LABELS).get(SystemLabels.LABEL_VOLUME_CLEANUP_STRATEGY); String behavior = b != null ? b.toString() : VOLUME_CLEANUP_STRATEGY_UNNAMED; Set<? extends Volume> volumes = volumeDao.findNonremovedVolumesWithNoOtherMounts(instance.getId()); for (Volume v : volumes) { String volumeBehavior = migrateVolume(instance, v, behavior); if (VOLUME_CLEANUP_STRATEGY_NONE.equals(volumeBehavior) || (!VOLUME_CLEANUP_STRATEGY_UNNAMED.equals(volumeBehavior) && !VOLUME_CLEANUP_STRATEGY_ALL.equals(volumeBehavior))) { continue; } if (VOLUME_CLEANUP_STRATEGY_UNNAMED.equals(volumeBehavior) && ((StringUtils.length(v.getName()) != 64 || !StringUtils.isAlphanumeric(v.getName()))) && !StringUtils.startsWith(v.getName(), "/")) { continue; } if (CommonStatesConstants.ACTIVE.equals(v.getState()) || CommonStatesConstants.ACTIVATING.equals(v.getState())) { objectProcessManager.scheduleStandardProcess(StandardProcess.DEACTIVATE, v, ProcessUtils.chainInData(state.getData(), VolumeConstants.PROCESS_DEACTIVATE, VolumeConstants.PROCESS_REMOVE)); } else { objectProcessManager.scheduleStandardProcess(StandardProcess.REMOVE, v, state.getData()); } } } /* * Deal with logic where we would set cleanup strategy to none for back populated containers. Now * we do this with a native flag on the volume so we know not to send an event. */ private String migrateVolume(Instance instance, Volume volume, String behavior) { if (!VOLUME_CLEANUP_STRATEGY_NONE.equals(behavior) || !instance.getNativeContainer()) { return behavior; } if (volume.getUri() == null || !volume.getUri().startsWith(VolumeConstants.FILE_PREFIX)) { return behavior; } behavior = VOLUME_CLEANUP_STRATEGY_UNNAMED; if (!DataAccessor.fieldBool(volume, VolumeConstants.FIELD_DOCKER_IS_NATIVE)) { objectManager.setFields(volume, VolumeConstants.FIELD_DOCKER_IS_NATIVE, true); } return behavior; } protected void network(Instance instance, Map<String, Object> data) { List<Nic> nics = getObjectManager().children(instance, Nic.class); for (Nic nic : nics) { remove(nic, data); } } public InstanceStop getInstanceStop() { return instanceStop; } @Inject public void setInstanceStop(InstanceStop instanceStop) { this.instanceStop = instanceStop; } public GenericMapDao getMapDao() { return mapDao; } @Inject public void setMapDao(GenericMapDao mapDao) { this.mapDao = mapDao; } }