/******************************************************************************* * Copyright (c) 2015 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package com.openshift.internal.restclient.capability.resources; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.openshift.restclient.IClient; import com.openshift.restclient.OpenShiftException; import com.openshift.restclient.capability.resources.IRSyncable; /** * Port forwarding implementation that wraps the OpenShift binary * * @author Andre Dietisheim * */ public class OpenShiftBinaryRSync extends AbstractOpenShiftBinaryCapability implements IRSyncable { private static final Logger LOG = LoggerFactory.getLogger(OpenShiftBinaryRSync.class); private static final long WAIT_FOR_EXIT_TIMEOUT = 5; // mins private Peer source; private Peer destination; private final Executor executor = Executors.newCachedThreadPool(); /** * Constructor. * @param client the client to connect to OpenShift. */ public OpenShiftBinaryRSync(final IClient client) { super(client); } @Override public InputStream sync(final Peer source, final Peer destination, final OpenShiftBinaryOption... options) throws OpenShiftException { this.source = source; this.destination = destination; start(options); // monitor the process completion in a separate thread this.executor.execute(() -> { try { this.getProcess().waitFor(); } catch (InterruptedException e) { throw new OpenShiftException("Error occurred while waiting for rsync operation to complete", e); } }); return getProcess().getInputStream(); } @Override public boolean isDone() { return !getProcess().isAlive(); } @Override public int exitValue() { return getProcess().exitValue(); } @Override public void await() throws InterruptedException { try { if (getProcess() == null) { throw new OpenShiftException("Could not sync %s to %s, no process was launched.", destination); } if (!getProcess().waitFor(WAIT_FOR_EXIT_TIMEOUT, TimeUnit.MINUTES)) { throw new OpenShiftException("Syncing %s to %s did not terminate within %d minutes.", source, destination, WAIT_FOR_EXIT_TIMEOUT); } if (getProcess().exitValue() != 0) { String errorMessage = getErrorMessage(getProcess().getErrorStream()); throw new OpenShiftException("Syncing %s to %s failed" + (StringUtils.isBlank(errorMessage) ? "" : ": %s"), source, destination, errorMessage); } } catch (InterruptedException e) { throw new OpenShiftException(e, "Syncing %s to %s was interrupted.", source, destination); } } private static String getErrorMessage(InputStream errorStream) { try { return IOUtils.toString(errorStream); } catch (IOException e) { LOG.error("Could not retrieve error message from process", e); return null; } } @Override protected void cleanup() { this.source = null; this.destination = null; } @Override protected boolean validate() { return source != null && destination != null && hasPodPeer(source, destination); } private static boolean hasPodPeer(Peer source, Peer destination) { return source.isPod() || destination.isPod(); } @Override public boolean isSupported() { return true; } @Override public String getName() { return OpenShiftBinaryRSync.class.getSimpleName(); } @Override protected String buildArgs(final List<OpenShiftBinaryOption> options) { final StringBuilder argsBuilder = new StringBuilder("rsync "); argsBuilder.append(getTokenFlag()).append(getServerFlag()); if(options.contains(OpenShiftBinaryOption.SKIP_TLS_VERIFY)) { argsBuilder.append(getSkipTlsVerifyFlag()); } if(options.contains(OpenShiftBinaryOption.EXCLUDE_GIT_FOLDER)) { argsBuilder.append(getGitFolderExclusionFlag()); } if(options.contains(OpenShiftBinaryOption.NO_PERMS)) { argsBuilder.append(getNoPermsFlags()); } if(options.contains(OpenShiftBinaryOption.DELETE)) { argsBuilder.append(getDeleteFlags()); } argsBuilder.append(source.getParameter()).append(" ") .append(destination.getParameter()); return argsBuilder.toString(); } }