Support sudo for installing plugins, but warn about it

This commit is contained in:
Gina Häußge 2015-09-28 19:53:30 +02:00
parent 2f24e73fcb
commit 1178fe9e95
5 changed files with 78 additions and 9 deletions

View file

@ -38,11 +38,25 @@ For information about how to go about contributions of any kind, please see the
Installation
------------
Installation instructions for installing from source for different operating systems can be found [on the wiki](https://github.com/foosel/OctoPrint/wiki#assorted-guides).
Installation instructions for installing from source for different operating
systems can be found [on the wiki](https://github.com/foosel/OctoPrint/wiki#assorted-guides).
If you want to run OctoPrint on a Raspberry Pi you might want to take a look at [OctoPi](https://github.com/guysoft/OctoPi)
which is a custom SD card image that includes OctoPrint plus dependencies.
The generic steps that should basically be done regardless of operating system
and runtime environment are the following (as *regular
user*, please keep your hands *off* of the `sudo` command here!) - this assumes
you already have Python 2.7, pip and virtualenv set up:
1. Checkout OctoPrint: `git clone https://github.com/foosel/OctoPrint.git`
2. Change into the OctoPrint folder: `cd OctoPrint`
3. Create a user-owned virtual environment therein: `virtualenv --system-site-packages venv`
4. Install OctoPrint *into that virtual environment*: `./venv/bin/python setup.py install`
You may then start the OctoPrint server via `/path/to/OctoPrint/venv/bin/octoprint`, see [Usage](#usage)
for details.
After installation, please make sure you follow the first-run wizard and set up
access control as necessary. If you want to not only be notified about new
releases but also be able to automatically upgrade to them from within

View file

@ -82,6 +82,7 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin,
repository="http://plugins.octoprint.org/plugins.json",
repository_ttl=24*60,
pip=None,
pip_args=None,
dependency_links=False,
hidden=[]
)
@ -187,7 +188,9 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin,
pip=dict(
available=self._pip_caller.available,
command=self._pip_caller.command,
version=str(self._pip_caller.version)
version=str(self._pip_caller.version),
use_sudo=self._pip_caller.use_sudo,
additional_args=self._settings.get(["pip_args"])
))
def on_api_command(self, command, data):
@ -447,6 +450,10 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin,
if self._pip_caller < self._pip_version_dependency_links:
args.remove("--process-dependency-links")
additional_args = self._settings.get(["pip_args"])
if additional_args:
args.append(additional_args)
return self._pip_caller.execute(*args)
def _log_message(self, *lines):

View file

@ -9,6 +9,7 @@ $(function() {
self.config_repositoryUrl = ko.observable();
self.config_repositoryTtl = ko.observable();
self.config_pipCommand = ko.observable();
self.config_pipAdditionalArgs = ko.observable();
self.configurationDialog = $("#settings_plugin_pluginmanager_configurationdialog");
@ -81,6 +82,8 @@ $(function() {
self.pipAvailable = ko.observable(false);
self.pipCommand = ko.observable();
self.pipVersion = ko.observable();
self.pipUseSudo = ko.observable();
self.pipAdditionalArgs = ko.observable();
self.working = ko.observable(false);
self.workingTitle = ko.observable();
@ -208,9 +211,13 @@ $(function() {
if (data.available) {
self.pipCommand(data.command);
self.pipVersion(data.version);
self.pipUseSudo(data.use_sudo);
self.pipAdditionalArgs(data.additional_args);
} else {
self.pipCommand(undefined);
self.pipVersion(undefined);
self.pipUseSudo(undefined);
self.pipAdditionalArgs(undefined);
}
};
@ -392,12 +399,18 @@ $(function() {
repositoryTtl = null;
}
var pipArgs = self.config_pipAdditionalArgs();
if (pipArgs != undefined && pipArgs.trim() == "") {
pipArgs = null;
}
var data = {
plugins: {
pluginmanager: {
repository: repository,
repository_ttl: repositoryTtl,
pip: pipCommand
pip: pipCommand,
pip_args: pipArgs
}
}
};
@ -412,6 +425,7 @@ $(function() {
self.config_repositoryUrl(self.settingsViewModel.settings.plugins.pluginmanager.repository());
self.config_repositoryTtl(self.settingsViewModel.settings.plugins.pluginmanager.repository_ttl());
self.config_pipCommand(self.settingsViewModel.settings.plugins.pluginmanager.pip());
self.config_pipAdditionalArgs(self.settingsViewModel.settings.plugins.pluginmanager.pip_args());
};
self.installed = function(data) {

View file

@ -6,14 +6,25 @@
{% macro pluginmanager_nopip() %}
<div class="alert" data-bind="visible: !pipAvailable()">{% trans %}
The <code>pip</code> command could not be detected automatically,
please configure it manually. No installation and uninstallation of plugin
The <code>pip</code> command could not be found.
Please configure it manually. No installation and uninstallation of plugin
packages is possible while <code>pip</code> is unavailable.
{% endtrans %}</div>
{% endmacro %}
{% macro pluginmanager_sudopip() %}
<div class="alert alert-error" data-bind="visible: pipUseSudo()">{% trans %}
The <code>pip</code> command is configured to use <code>sudo</code>. This
is <strong>not</strong> recommended due to security reasons. It is <strong>strongly</strong>
suggested you install OctoPrint under a
<a href="https://github.com/foosel/OctoPrint/#installation">user-owned virtual environment</a>
so that the use of <code>sudo</code> is not needed for plugin management.
{% endtrans %}</div>
{% endmacro %}
{{ pluginmanager_printing() }}
{{ pluginmanager_nopip() }}
{{ pluginmanager_sudopip() }}
<div class="pull-right">
<button class="btn btn-small" data-bind="click: function() { $root.showPluginSettings(); }" title="{{ _('Plugin Configuration') }}"><i class="icon-wrench"></i></button>
@ -61,7 +72,7 @@
<button class="btn btn-block" data-bind="click: $root.showRepository">{{ _('Get More...') }}</button>
<p class="muted" data-bind="visible: pipAvailable()">
<small>Using pip at "<span data-bind="text: pipCommand"></span>" (Version <span data-bind="text: pipVersion"></span>)</small>
<small>Using pip at "<span data-bind="text: pipCommand"></span>" (Version <span data-bind="text: pipVersion"></span><span data-bind="visible: pipAdditionalArgs">, additional arguments: <span data-bind="text: pipAdditionalArgs"></span></span>)</small>
</p>
<div id="settings_plugin_pluginmanager_workingdialog" class="modal hide fade">
@ -85,6 +96,7 @@
<div class="modal-body">
{{ pluginmanager_printing() }}
{{ pluginmanager_nopip() }}
{{ pluginmanager_sudopip() }}
<h4 style="position: relative">
{{ _('... from the <a href="%(url)s" target="_blank">Plugin Repository</a>', url='http://plugins.octoprint.org') }}
<a class="dropdown-toggle pull-right" data-toggle="dropdown" href="#">
@ -200,6 +212,12 @@
<span class="help-inline">{{ _('<strong>Only</strong> set this if OctoPrint cannot autodetect the path to <code>pip</code> to use for managing plugins.') }}</span>
</div>
</div>
<div class="control-group" title="{{ _('Additional arguments for pip command. You should normally not have to change this.') }}">
<label class="control-label">{{ _('Additional pip arguments') }}</label>
<div class="controls">
<input type="text" class="input-block-level" data-bind="value: config_pipAdditionalArgs">
</div>
</div>
<div class="control-group" title="{{ _('URL of the Plugin Repository to use. You should normally not have to change this.') }}">
<label class="control-label">{{ _('Repository URL') }}</label>
<div class="controls">

View file

@ -26,6 +26,7 @@ class PipCaller(object):
self._command = None
self._version = None
self._use_sudo = False
self.trigger_refresh()
@ -53,13 +54,17 @@ class PipCaller(object):
def version(self):
return self._version
@property
def use_sudo(self):
return self._use_sudo
@property
def available(self):
return self._command is not None
def trigger_refresh(self):
try:
self._command, self._version = self._find_pip()
self._command, self._version, self._use_sudo = self._find_pip()
except:
self._logger.exception("Error while discovering pip command")
self._command = None
@ -74,6 +79,8 @@ class PipCaller(object):
raise UnknownPip()
command = [self._command] + list(args)
if self._use_sudo:
command = ["sudo"] + command
joined_command = " ".join(command)
self._logger.debug(u"Calling: {}".format(joined_command))
@ -120,6 +127,11 @@ class PipCaller(object):
def _find_pip(self):
pip_command = self.configured
if pip_command is not None and pip_command.startswith("sudo "):
pip_command = pip_command[len("sudo "):]
pip_sudo = True
else:
pip_sudo = False
pip_version = None
if pip_command is None:
@ -152,7 +164,11 @@ class PipCaller(object):
if pip_command is not None:
self._logger.debug("Found pip at {}, going to figure out its version".format(pip_command))
p = sarge.run([pip_command, "--version"], stdout=sarge.Capture(), stderr=sarge.Capture())
sarge_command = [pip_command, "--version"]
if pip_sudo:
sarge_command = ["sudo"] + sarge_command
p = sarge.run(sarge_command, stdout=sarge.Capture(), stderr=sarge.Capture())
if p.returncode != 0:
self._logger.warn("Error while trying to run pip --version: {}".format(p.stderr.text))
@ -182,7 +198,7 @@ class PipCaller(object):
else:
self._logger.info("Found pip at {}, version is {}".format(pip_command, version_segment))
return pip_command, pip_version
return pip_command, pip_version, pip_sudo
def _log_stdout(self, *lines):
self.on_log_stdout(*lines)