Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Commit

Permalink
Enhancement: support shell patterns on inline option of shell provi…
Browse files Browse the repository at this point in the history
…sioner. (#70)
  • Loading branch information
lingxiaoyang authored and Virgil Dupras committed May 12, 2017
1 parent 3b87dc6 commit d32977a
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 15 deletions.
10 changes: 3 additions & 7 deletions docs/provisioners/shell.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,12 @@ Just append a ``shell`` provisioning operation to your LXDock file as follows:
provisioning:
- type: shell
inline: echo "Hello, World!"
inline: cd /tmp && echo "Here's the PATH" $$PATH >> test.txt
.. note::

Keep in mind that the shell provisioner will use the LXD's ``exec`` method in order to run your
commands on containers (the same method used by the ``lxc exec`` command). This means that common
shell patterns (like file redirects, ``|``, ``>``, ``<``, ...) won't work because the ``exec``
method doesn't use a shell (so the kernel will not be able to understand these shell patterns).
The only way to overcome this is to put things like ``sh -c 'ls -l > /tmp/test'`` in your
``inline`` options.
The inline command is executed by ``sh -c 'command_line'``. Keep in mind that dollar sign ``$``
means string interpolation in YAML and it is necessary to put ``$$`` to escape the dollar sign.

Required options
----------------
Expand Down
2 changes: 2 additions & 0 deletions docs/release_notes/v0.3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ New features
* Add support for variable interpolation in LXDock files
(`#65 <https://github.com/lxdock/lxdock/pull/65>`_,
`#75 <https://github.com/lxdock/lxdock/pull/75>`_)
* Add support for shell patterns on the ``inline`` option of shell provisioner
(`#70 <https://github.com/lxdock/lxdock/pull/70>`_)
* Add support for passing a command line with ``lxdock shell``
(`#67 <https://github.com/lxdock/lxdock/pull/67>`_)
* Add basic support for LXC profile in LXDock files
Expand Down
3 changes: 2 additions & 1 deletion lxdock/hosts/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import logging
import os
import platform
import shlex
import subprocess
from pathlib import Path

Expand Down Expand Up @@ -124,6 +125,6 @@ def give_mapped_user_access_to_share(self, source, userpath=None):

def run(self, cmd_args):
""" Runs the specified command on the host. """
cmd = ' '.join(cmd_args)
cmd = ' '.join(map(shlex.quote, cmd_args))
logger.debug('Running {0} on the host'.format(cmd))
subprocess.Popen(cmd, shell=True).wait()
3 changes: 1 addition & 2 deletions lxdock/provisioners/shell.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import os
import shlex

from voluptuous import Any, Exclusive, IsFile

Expand Down Expand Up @@ -33,7 +32,7 @@ def provision(self):
elif 'inline' in self.options:
# Final case: we run a command directly inside the container or outside.
host_or_guest = getattr(self, self._side)
host_or_guest.run(shlex.split(self.options['inline']))
host_or_guest.run(['sh', '-c', self.options['inline']])

##################################
# PRIVATE METHODS AND PROPERTIES #
Expand Down
17 changes: 16 additions & 1 deletion tests/integration/test_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def test_can_try_to_halt_a_container_that_is_already_stopped(self, persistent_co
persistent_container.halt()
assert persistent_container._container.status_code == constants.CONTAINER_STOPPED

def test_can_provision_a_container(self):
def test_can_provision_a_container_ansible(self):
container_options = {
'name': self.containername('willprovision'), 'image': 'ubuntu/xenial', 'mode': 'pull',
'provisioning': [
Expand All @@ -86,6 +86,21 @@ def test_can_provision_a_container(self):
assert container._container.config['user.lxdock.provisioned'] == 'true'
assert container._container.files.get('/dummytest').strip() == b'dummytest'

def test_can_provision_a_container_shell_inline(self):
container_options = {
'name': self.containername('willprovision'), 'image': 'ubuntu/xenial', 'mode': 'pull',
'environment': {'PATH': '/dummy_test:/bin:/usr/bin:/usr/local/bin'},
'provisioning': [
{'type': 'shell',
'inline': """touch f && echo "Here's the PATH" $PATH >> /tmp/test.txt""", }
],
}
container = Container('myproject', THIS_DIR, self.client, **container_options)
container.up()
assert container._container.config['user.lxdock.provisioned'] == 'true'
assert container._container.files.get('/tmp/test.txt').strip() == (
b"Here's the PATH /dummy_test:/bin:/usr/bin:/usr/local/bin")

@unittest.mock.patch('subprocess.call')
def test_can_open_a_shell_for_the_root_user(self, mocked_call, persistent_container):
persistent_container.shell()
Expand Down
12 changes: 8 additions & 4 deletions tests/unit/provisioners/test_shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,24 @@ def test_can_run_commands_on_the_host_side(self, mock_popen):
host = Host(unittest.mock.Mock())
guest = DebianGuest(unittest.mock.Mock())
provisioner = ShellProvisioner(
'./', host, guest, {'inline': 'touch f', 'side': 'host', })
'./', host, guest, {
'inline': """touch f && echo "Here's the PATH" $PATH >> /tmp/test.txt""",
'side': 'host', })
provisioner.provision()
assert mock_popen.call_args[0] == ('touch f', )
assert mock_popen.call_args[0] == (
"""sh -c 'touch f && echo "Here'"'"'s the PATH" $PATH >> /tmp/test.txt'""", )

def test_can_run_commands_on_the_guest_side(self):
lxd_container = unittest.mock.Mock()
lxd_container.execute.return_value = ('ok', 'ok', '')
host = Host(unittest.mock.Mock())
guest = DebianGuest(lxd_container)
cmd = """touch f && echo "Here's the PATH" $PATH >> /tmp/test.txt"""
provisioner = ShellProvisioner(
'./', host, guest, {'inline': 'echo TEST'})
'./', host, guest, {'inline': cmd})
provisioner.provision()
assert lxd_container.execute.call_count == 1
assert lxd_container.execute.call_args_list[0][0] == (['echo', 'TEST'], )
assert lxd_container.execute.call_args_list[0][0] == (['sh', '-c', cmd], )

@unittest.mock.patch('subprocess.Popen')
def test_can_run_a_script_on_the_host_side(self, mock_popen):
Expand Down

0 comments on commit d32977a

Please sign in to comment.