/*******************************************************************************
* 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();
}
}