All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.galasa.docker.internal.DockerVolumeResourceMonitor Maven / Gradle / Ivy

The newest version!
/*
 * Copyright contributors to the Galasa project
 *
 * SPDX-License-Identifier: EPL-2.0
 */
package dev.galasa.docker.internal;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import dev.galasa.framework.spi.ConfigurationPropertyStoreException;
import dev.galasa.framework.spi.DssDeletePrefix;
import dev.galasa.framework.spi.IConfigurationPropertyStoreService;
import dev.galasa.framework.spi.IDynamicStatusStoreService;
import dev.galasa.framework.spi.IFramework;
import dev.galasa.framework.spi.IResourceManagement;
import dev.galasa.http.HttpClientResponse;
import dev.galasa.http.IHttpClient;
import dev.galasa.http.StandAloneHttpClient;

public class DockerVolumeResourceMonitor implements Runnable {
    private final IFramework framework;
    private final IResourceManagement resourceManagement;
    private final IDynamicStatusStoreService dss;
    private final IConfigurationPropertyStoreService cps;
    private final Log logger = LogFactory.getLog(DockerVolumeResourceMonitor.class);

    private Map dockerEngines = new HashMap<>();

    private final Pattern slotRunPattern = Pattern.compile("^volume\\.(\\w+)\\.engine");

    public DockerVolumeResourceMonitor(IFramework framework, IResourceManagement resourceManagement,
            IDynamicStatusStoreService dss, DockerResourceManagement dockerResourceManagement,
            IConfigurationPropertyStoreService cps) {
        this.framework = framework;
        this.dss = dss;
        this.cps = cps;
        this.resourceManagement = resourceManagement;

        this.logger.info("Docker volume resource monitor intialised");
    }

    @Override
    public void run() {
        logger.info("Starting search for orphaned volumes.");
        try {
            updateDockerEngines();
        
            Set activeRunNames = this.framework.getFrameworkRuns().getActiveRunNames();

            removeStaleProperties(activeRunNames);
            checkForStaleVolumes(activeRunNames);
        } catch (Exception e)  {
            logger.error("Volume monitor failed: ", e);
        }
        
        logger.info("Finished search for orphaned volumes.");
    }

    public void checkForStaleVolumes(Set activeRunNames) {
        for (String engine : this.dockerEngines.keySet()) {
            List volumes = getOrphanedVolumes(engine, this.dockerEngines.get(engine), activeRunNames);
            logger.info("Engine " + engine + " has " + volumes.size() + " orphaned volumes found");
            if (volumes.size() > 0) {
                killVolumes(volumes, this.dockerEngines.get(engine));
            }
        }
    }

    public List getOrphanedVolumes(String engineName, IHttpClient client, Set activeRunNames) {
        List orphanedVolumes = new ArrayList<>();
        try {
            HttpClientResponse resp = client.getJson("/volumes");
            JsonObject json = resp.getContent();
            if (resp.getStatusCode() != 200) {
                logger.error("Something went wrong when retrieving volumes: " + resp.getStatusLine());
                return orphanedVolumes;
            }
            JsonArray volumes = json.getAsJsonArray("Volumes");

            for (int i=0; i<=volumes.size(); i++) {
                JsonObject volJson = volumes.get(i).getAsJsonObject();
                if (volJson.get("Labels").isJsonNull()) {
                    continue;
                }
                JsonObject labels = volJson.get("Labels").getAsJsonObject();
                if (!(labels.get("GALASA") == null)) {
                    if (!activeRunNames.contains(labels.get("RUN_ID").getAsString())){
                        logger.info("Found orphaned volume: " + volJson.get("Name").getAsString());
                        orphanedVolumes.add(volJson.get("Name").getAsString());
                    }
                }
            }
            
        } catch (Exception e) {
            logger.error("Failed to locate orphaned volumes");
        }
        return orphanedVolumes;
    }

    public void killVolumes(List volumes, IHttpClient client) {
        try {
            for (String volume : volumes) {
                HttpClientResponse resp = client.deleteText("/volumes/"+volume+"?force=true");
                if (resp.getStatusCode() != 204) {
                    logger.error("Something went wrong when removing volume (Likely the a container still mounted): " + resp.getStatusLine());
                    return;
                }
            }
        } catch (Exception e) {
            logger.error("Failed to delete volumes");
        }
    }

    public void removeStaleProperties (Set activeRunNames) {

        try{
            Map volumeProps = dss.getPrefix("slot");
            

            for(String key : volumeProps.keySet()) {
                Matcher matcher = slotRunPattern.matcher(key);
				if (matcher.find()) {
                    String volumeName = matcher.group(0);
                    String runID = volumeProps.get("dss.docker.volume." + volumeName + "run");
                    if (activeRunNames.contains(runID)) {
                        break;
                    } else {
                        logger.info("Stale properties found, cleaning up propeties for volume: " + volumeName);
                        dss.performActions(new DssDeletePrefix("volume."+volumeName));
                    }
                } 
            }
            
        } catch (Exception e) {
            logger.error("Failed to clean DSS volume properties");
        }
    }

    /**
     * Looks at CPS for the docker Engines available
     * @return
     */
    private void updateDockerEngines() {
        try { 
            // This will have to be changed if we support engine clusters
        	String[] tags;
            String enginesTags = cps.getProperty("default", "engines");
            if (enginesTags == null) {
            	logger.info("No default Docker engines defined, moving on");
            	return;
            }
            tags = enginesTags.split(",");
            for (String engine : tags) {
                if (this.dockerEngines.get(engine) == null) {
                    String hostname = cps.getProperty("engine", "hostname", engine);
                    String port = cps.getProperty("engine", "port", engine);
    
                    IHttpClient client = StandAloneHttpClient.getHttpClient(3600, logger);
                    client.setURI(new URI(hostname+":"+port));
                    this.dockerEngines.put(engine, client);
                }
            }
        } catch (ConfigurationPropertyStoreException | URISyntaxException e) {
            logger.error("Failed to get Docker engines.", e);
        }
    }
    
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy