/* * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jetbrains.python.packaging; import com.google.common.base.Function; import com.google.common.collect.Collections2; import com.intellij.execution.ExecutionException; import com.intellij.execution.process.ProcessOutput; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.SdkAdditionalData; import com.intellij.openapi.util.text.StringUtil; import com.intellij.remote.RemoteFile; import com.intellij.remote.RemoteSdkAdditionalData; import com.intellij.remote.RemoteSdkCredentials; import com.intellij.remote.VagrantNotStartedException; import com.intellij.util.ArrayUtil; import com.intellij.util.PathMappingSettings; import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase; import com.jetbrains.python.remote.PythonRemoteInterpreterManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; /** * @author vlan */ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { private static final String LAUNCH_VAGRANT = "launchVagrant"; public static final int ERROR_VAGRANT_NOT_LAUNCHED = 101; public static final int ERROR_REMOTE_ACCESS = 102; private static final Logger LOG = Logger.getInstance(PyRemotePackageManagerImpl.class); PyRemotePackageManagerImpl(@NotNull Sdk sdk) { super(sdk); } @Nullable @Override protected String getHelperPath(String helper) { final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData(); if (sdkData instanceof PyRemoteSdkAdditionalDataBase) { final PyRemoteSdkAdditionalDataBase remoteSdkData = (PyRemoteSdkAdditionalDataBase)mySdk.getSdkAdditionalData(); try { final RemoteSdkCredentials remoteSdkCredentials = remoteSdkData.getRemoteSdkCredentials(false); if (!StringUtil.isEmpty(remoteSdkCredentials.getHelpersPath())) { return new RemoteFile(remoteSdkCredentials.getHelpersPath(), helper).getPath(); } else { return null; } } catch (Exception e) { LOG.error(e); } } return null; } @Override protected ProcessOutput getProcessOutput(@NotNull String helperPath, @NotNull List args, boolean askForSudo, @Nullable String workingDir) throws PyExternalProcessException { final String homePath = mySdk.getHomePath(); if (homePath == null) { throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Cannot find interpreter for SDK"); } final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData(); if (sdkData instanceof PyRemoteSdkAdditionalDataBase) { //remote interpreter RemoteSdkCredentials remoteSdkCredentials; try { remoteSdkCredentials = ((RemoteSdkAdditionalData)sdkData).getRemoteSdkCredentials(false); } catch (InterruptedException e) { LOG.error(e); remoteSdkCredentials = null; } catch (final ExecutionException e) { if (e.getCause() instanceof VagrantNotStartedException) { throw new PyExternalProcessException(ERROR_VAGRANT_NOT_LAUNCHED, helperPath, args, "Vagrant instance is down. Launch vagrant") .withHandler(LAUNCH_VAGRANT, new Runnable() { @Override public void run() { final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance(); if (manager != null) { try { manager.runVagrant(((VagrantNotStartedException)e.getCause()).getVagrantFolder()); clearCaches(); } catch (ExecutionException e1) { throw new RuntimeException(e1); } } } }); } else { throw new PyExternalProcessException(ERROR_REMOTE_ACCESS, helperPath, args, e.getMessage()); } } final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance(); if (manager != null && remoteSdkCredentials != null) { final List cmdline = new ArrayList(); cmdline.add(homePath); cmdline.add(RemoteFile.detectSystemByPath(homePath).createRemoteFile(helperPath).getPath()); cmdline.addAll(Collections2.transform(args, new Function() { @Override public String apply(@Nullable String input) { return quoteIfNeeded(input); } })); try { if (askForSudo) { askForSudo = !manager.ensureCanWrite(null, remoteSdkCredentials, remoteSdkCredentials.getInterpreterPath()); } ProcessOutput processOutput; do { PathMappingSettings mappings = manager.setupMappings(null, (PyRemoteSdkAdditionalDataBase)sdkData, null); processOutput = manager.runRemoteProcess(null, remoteSdkCredentials, mappings, ArrayUtil.toStringArray(cmdline), workingDir, askForSudo); if (askForSudo && processOutput.getStderr().contains("sudo: 3 incorrect password attempts")) { continue; } break; } while (true); return processOutput; } catch (ExecutionException e) { throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Error running SDK: " + e.getMessage(), e); } } else { throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, PythonRemoteInterpreterManager.WEB_DEPLOYMENT_PLUGIN_IS_DISABLED); } } else { throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Invalid remote SDK"); } } @Override protected void subscribeToLocalChanges(Sdk sdk) { // Local VFS changes aren't needed } @Override protected void installManagement(@NotNull String name) throws PyExternalProcessException { super.installManagement(name); // TODO: remove temp directory for remote interpreter } private static String quoteIfNeeded(String arg) { return arg.replace("<", "\\<").replace(">", "\\>"); //TODO: move this logic to ParametersListUtil.encode } }