/******************************************************************************* * 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.util.Arrays; import java.util.List; 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.OpenShiftContext; import com.openshift.restclient.OpenShiftException; import com.openshift.restclient.capability.IBinaryCapability; import com.openshift.restclient.capability.resources.LocationNotFoundException; /** * Capability that wraps the OpenShift binary * * @author Jeff Cantrill * */ public abstract class AbstractOpenShiftBinaryCapability implements IBinaryCapability { private static final Logger LOG = LoggerFactory.getLogger(AbstractOpenShiftBinaryCapability.class); private Process process; private IClient client; protected AbstractOpenShiftBinaryCapability(IClient client) { this.client = client; } /** * Cleanup required when stopping the process */ protected abstract void cleanup(); /** * Validate arguments before starting process * @return true if start should continue; false otherwise; */ protected abstract boolean validate(); /** * Callback for building args to be sent to the {@code oc} command. * @return the String representation of all the arguments to use when running the {@code oc} command. */ protected abstract String buildArgs(final List<OpenShiftBinaryOption> options); protected IClient getClient() { return client; } protected AbstractOpenShiftBinaryCapability() { addShutdownHook(); } protected Process getProcess() { return process; } private void addShutdownHook() { Runnable runnable = new Runnable() { @Override public void run() { stop(); } }; Runtime.getRuntime().addShutdownHook(new Thread(runnable)); } protected String getUserFlag() { final StringBuilder argBuilder = new StringBuilder(); argBuilder.append("--user=").append(client.getAuthorizationContext().getUserName()).append(" "); return argBuilder.toString(); } /** * @return */ protected String getServerFlag() { final StringBuilder argBuilder = new StringBuilder(); argBuilder.append("--server=").append(client.getBaseURL()).append(" "); return argBuilder.toString(); } /** * Adds the authentication token * @return the command-line argument to use the current token */ protected String getTokenFlag() { return new StringBuilder("--token=") .append(client.getAuthorizationContext().getToken()) .append(" ").toString(); } /** * @return the command-line flag to use insecure connection (skip TLS verification) */ protected String getSkipTlsVerifyFlag() { return "--insecure-skip-tls-verify=true "; } /** * @return the command-line flag to exclude some files/directories that do * not need to be synchronized between the remote pod and the local * deployment directory. */ protected String getGitFolderExclusionFlag() { // no support for multiple exclusion, so excluding '.git' only for now // see https://github.com/openshift/origin/issues/8223 return "--exclude='.git' "; } /** * @return the command-line flag to avoid transferring permissions. */ protected String getNoPermsFlags() { return "--no-perms=true "; } /** * @return the command-line flag to delete extraneous file from destination directories. */ protected String getDeleteFlags() { return "--delete "; } /** * Starts the {@link Process} to run the {@code oc} command. * @param options the command line options */ public final void start(final OpenShiftBinaryOption... options) { String location = getOpenShiftBinaryLocation(); if(!validate()) { return; } startProcess(location, options); } private void startProcess(final String location, final OpenShiftBinaryOption... options) { String cmdLine = new StringBuilder(location).append(' ').append(buildArgs(Arrays.asList(options))).toString(); String[] args = StringUtils.split(cmdLine, " "); ProcessBuilder builder = new ProcessBuilder(args); builder.environment().remove("KUBECONFIG"); LOG.debug("OpenShift binary args: {}", builder.command()); try { process = builder.start(); checkProcessIsAlive(); } catch (IOException e) { LOG.error("Could not start process for {}.", new Object[]{ getName(), e }); throw new OpenShiftException(e, "Does your OpenShift binary location exist? Error starting process: %s", e.getMessage()); } } private void checkProcessIsAlive() throws IOException { try { // TODO: replace fixed wait with wait for process to be running Thread.sleep(1000); if(!process.isAlive() && process.exitValue() != 0) { throw new OpenShiftException("OpenShiftBinaryCapability process exited: %s", IOUtils.toString(process.getErrorStream())); } } catch (InterruptedException e) { if(!process.isAlive() && process.exitValue() != 0) { throw new OpenShiftException("OpenShiftBinaryCapability process exited: %s", IOUtils.toString(process.getErrorStream())); } } } /** * Stops the {@link Process} running the {@code oc} command. */ public final synchronized void stop() { if(process == null) return; cleanup(); if(!process.isAlive()) { final int exitValue = process.exitValue(); LOG.debug("OpenShiftBinaryCapability process exit code {}", exitValue); if(exitValue != 0) { try { LOG.debug("OpenShiftBinaryCapability process error stream", IOUtils.toString(process.getErrorStream())); } catch (IOException e) { LOG.debug("IOException trying to debug the process error stream", e); } } process = null; return; } process.destroyForcibly(); } protected String getOpenShiftBinaryLocation() { //Check the ThreadLocal for oc binary String location = OpenShiftContext.get().get(OPENSHIFT_BINARY_LOCATION); if (StringUtils.isBlank(location)) { //Fall back to System property location = System.getProperty(OPENSHIFT_BINARY_LOCATION); } if(StringUtils.isBlank(location)) { throw new LocationNotFoundException( String.format("The OpenShift 'oc' binary location was not specified. Set the property %s", OPENSHIFT_BINARY_LOCATION)); } location = addQuotesIfRequired(location); return location; } private String addQuotesIfRequired(String location) { if (!StringUtils.isEmpty(location) && location.contains(" ")) { location = "\"" + location + "\""; } return location; } }