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

dev.galasa.docker.internal.DockerExecImpl 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.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.List;


import com.google.gson.JsonObject;

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

import dev.galasa.docker.DockerManagerException;
import dev.galasa.docker.IDockerExec;
import dev.galasa.framework.spi.IFramework;
import dev.galasa.framework.spi.utils.GalasaGson;

/**
 * DockerExecImpl. An object passed back used to monitor and control the exec process on a container.
 * 
 *   
 */
public class DockerExecImpl implements IDockerExec {

    private final IFramework                                framework;
    private final DockerManagerImpl                         dockerManager;
    private final DockerContainerImpl                       dockerContainer;
    private final DockerEngineImpl                          dockerEngine;
    private final List                              commands;
    private final int                                       timeout;
    private final ExecThread                                execThread;
    private final String                                    id;
    
    private HttpURLConnection                               conn = null;
    private boolean                                         finished;
    private final StringBuffer                              outputBuffer = new StringBuffer();
    private long                                            exitCode = -1;
    private GalasaGson                                      gson = new GalasaGson();

    private static final Log                                logger = LogFactory.getLog(DockerExecImpl.class);

    /**
     * 
     * Creates the exec Json to be sent to docker engine.
     * 
     * @param framework
     * @param dockerManager
     * @param dockerContainer
     * @param timeout
     * @param commands
     * @throws DockerManagerException
     */
    public DockerExecImpl(IFramework framework, DockerManagerImpl dockerManager, DockerContainerImpl 
            dockerContainer, int timeout, String[] commands) throws DockerManagerException {
        this.framework                  = framework;
        this.dockerManager              = dockerManager;
        this.dockerContainer            = dockerContainer;
        this.timeout                    = timeout;
        this.commands                   = Arrays.asList(commands);
        this.dockerEngine               = dockerContainer.getDockerEngineImpl();

        try{
            ExecJson eJson = new ExecJson(false, true, true, true, this.commands);
            String json = gson.toJson(eJson);

            JsonObject cmd = gson.fromJson(json, JsonObject.class);

            JsonObject response = dockerEngine.sendExecCommands(dockerContainer.getContainerId(), cmd);
            if(response == null){
                throw new DockerManagerException("Did not receive a response from exec start for command");
            }

            // command ID not container ID
            id = response.get("Id").getAsString();
            if (id == null || id.trim().isEmpty()) {
                throw new DockerManagerException("Invalid response received from exec start for command - " + response.getAsString());
            }

            StringBuffer sb = new StringBuffer();
            for(String c : commands) {
                if (sb.length() > 0) {
                    sb.append(" ");
                }
                sb.append(c);
            }

            logger.info("Issuing command to Docker container '" + sb.toString() + "'");

            execThread = new ExecThread();
            execThread.start();
            logger.info("Command started");

        } catch(Exception e) {
            finished = true;
            throw new DockerManagerException("Failed to exec: ", e);
        }

    }

    /**
     * Standard wait with timeout for exec
     * 
     * @throws DockerManagerException
     */
    @Override
    public boolean waitForExec() throws DockerManagerException {
       return waitForExec(120000);
    }

    @Override
    public HttpURLConnection getConnection() {
        return conn;
    }

    /**
     * Wait for exec with specidied timeout
     * 
     * @param timeout
     * @throws DockerManagerException
     */
    @Override
    public boolean waitForExec(long timeout) throws DockerManagerException {
        long endTime = System.currentTimeMillis() + timeout;
        while(!finished && System.currentTimeMillis() < endTime) {
            try {
                Thread.sleep(100);
            } catch(InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new DockerManagerException("Wait for exec was interrupted", e);    
            }
        }
        return finished;
    }

    /**
     * Returns boolean if exec command is completed.
     */
    @Override
    public boolean isFinished() {
        return finished;
    }

    /**
     * Returns the last consoloe output line from the container.
     */
    @Override
    public String getCurrentOutput() {
        return outputBuffer.toString();
    }

    /**
     * Returns exitCdoe from the container.
     */
    @Override
    public long getExitCode() {
        return exitCode;
    }

    /**
     * A separate thread for the exec to the container to be performed in
     */
    private class ExecThread extends Thread {

        /**
         * Run the exec thread
         */
		@Override
		public void run() {
			InputStream is = null;
			OutputStream os = null;
			try {
				URL url = new URL(dockerEngine.getURI() + "/exec/" + id + "/start");
				conn = (HttpURLConnection) url.openConnection();

				conn.setConnectTimeout(timeout);
				conn.setReadTimeout(timeout);

				conn.setDoOutput(true);
				conn.setDoInput(true);

				conn.addRequestProperty("Content-Type", "application/json");

				conn.connect();

				os = conn.getOutputStream();
				os.write("{\"Detach\": false, \"Tty\": true}".getBytes());
				os.close();
				os = null;

				is = conn.getInputStream();
				byte[] buffer = new byte[1024];
				int len = 0;

				while((len = is.read(buffer)) >= 0) {
					String data = new String(buffer, 0, len);
					outputBuffer.append(data);
				}
				is.close();
				is = null;
				
				JsonObject status = dockerEngine.getExecInfo(id);
				String exitCodeObj = status.get("ExitCode").getAsString();
				if (exitCodeObj != null) {
					exitCode = Long.parseLong(exitCodeObj);
				}
                
				logger.debug("Command completed with exitcode " + exitCode);
				
				finished = true;
			} catch (Exception e) {
                logger.error("Failure during exec running", e);
                Thread.currentThread().interrupt();
			} finally {
                try{
					if (is!=null) {
						is.close();
					}
                    if (os!=null)  {
						os.close();
					}
                } catch (IOException e) {
                    logger.info("Failed to close stream, failing quietly: " + e);
                }
			}			
		}
    }
    
    /**
     * Expected json object for the docker engine API.
     */
    private class ExecJson {
        protected boolean attachStdIn;
        protected boolean attachStdOut;
        protected boolean attachStdErr;
        protected boolean tty;
        protected List cmd;

        public ExecJson (boolean attachStdIn, boolean attachStdOut, boolean attachStdErr,
                 boolean tty, List commands) {
            this.attachStdIn    = attachStdIn;
            this.attachStdOut   = attachStdOut;
            this.attachStdErr   = attachStdErr;
            this.tty            = tty;
            this.cmd            = commands;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy