Skip to content

Commit

Permalink
s6 service module
Browse files Browse the repository at this point in the history
  • Loading branch information
skrobul committed Oct 20, 2015
1 parent b8271c7 commit f735792
Show file tree
Hide file tree
Showing 2 changed files with 340 additions and 0 deletions.
198 changes: 198 additions & 0 deletions salt/modules/s6.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# -*- coding: utf-8 -*-
'''
s6 service module
This module is compatible with the :mod:`service <salt.states.service>` states,
so it can be used to maintain services using the ``provider`` argument:
.. code-block:: yaml
myservice:
service:
- running
- provider: s6
Note that the ``enabled`` argument is not available with this provider.
:codeauthor: :email:`Marek Skrobacki <skrobul@skrobul.com>`
'''
from __future__ import absolute_import

# Import python libs
import os
import re

# Import salt libs
from salt.exceptions import CommandExecutionError

__func_alias__ = {
'reload_': 'reload'
}

VALID_SERVICE_DIRS = [
'/service',
'/etc/service',
]
SERVICE_DIR = None
for service_dir in VALID_SERVICE_DIRS:
if os.path.exists(service_dir):
SERVICE_DIR = service_dir
break


def _service_path(name):
'''
build service path
'''
if not SERVICE_DIR:
raise CommandExecutionError("Could not find service directory.")
return '{0}/{1}'.format(SERVICE_DIR, name)


def start(name):
'''
Starts service via s6
CLI Example:
.. code-block:: bash
salt '*' s6.start <service name>
'''
cmd = 's6-svc -u {0}'.format(_service_path(name))
return not __salt__['cmd.retcode'](cmd)


def stop(name):
'''
Stops service via s6
CLI Example:
.. code-block:: bash
salt '*' s6.stop <service name>
'''
cmd = 's6-svc -d {0}'.format(_service_path(name))
return not __salt__['cmd.retcode'](cmd)


def term(name):
'''
Send a TERM to service via s6
CLI Example:
.. code-block:: bash
salt '*' s6.term <service name>
'''
cmd = 's6-svc -t {0}'.format(_service_path(name))
return not __salt__['cmd.retcode'](cmd)


def reload_(name):
'''
Send a HUP to service via s6
CLI Example:
.. code-block:: bash
salt '*' s6.reload <service name>
'''
cmd = 's6-svc -h {0}'.format(_service_path(name))
return not __salt__['cmd.retcode'](cmd)


def restart(name):
'''
Restart service via s6. This will stop/start service
CLI Example:
.. code-block:: bash
salt '*' s6.restart <service name>
'''
cmd = 's6-svc -t {0}'.format(_service_path(name))
return not __salt__['cmd.retcode'](cmd)


def full_restart(name):
'''
Calls s6.restart() function
CLI Example:
.. code-block:: bash
salt '*' s6.full_restart <service name>
'''
restart(name)


def status(name, sig=None):
'''
Return the status for a service via s6, return pid if running
CLI Example:
.. code-block:: bash
salt '*' s6.status <service name>
'''
cmd = 's6-svstat {0}'.format(_service_path(name))
out = __salt__['cmd.run_stdout'](cmd)
try:
pid = re.search(r'up \(pid (\d+)\)', out).group(1)
except AttributeError:
pid = ''
return pid


def available(name):
'''
Returns ``True`` if the specified service is available, otherwise returns
``False``.
CLI Example:
.. code-block:: bash
salt '*' s6.available foo
'''
return name in get_all()


def missing(name):
'''
The inverse of s6.available.
Returns ``True`` if the specified service is not available, otherwise returns
``False``.
CLI Example:
.. code-block:: bash
salt '*' s6.missing foo
'''
return name not in get_all()


def get_all():
'''
Return a list of all available services
CLI Example:
.. code-block:: bash
salt '*' s6.get_all
'''
if not SERVICE_DIR:
raise CommandExecutionError("Could not find service directory.")
service_list = [dirname for dirname
in os.listdir(SERVICE_DIR)
if not dirname.startswith('.')]
return sorted(service_list)
142 changes: 142 additions & 0 deletions tests/unit/modules/s6_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Marek Skrobacki <skrobul@skrobul.com>`
'''

# Import Python Libs
from __future__ import absolute_import
import os

# Import Salt Testing Libs
from salttesting import TestCase, skipIf
from salttesting.mock import (
MagicMock,
patch,
NO_MOCK,
NO_MOCK_REASON
)

from salttesting.helpers import ensure_in_syspath

ensure_in_syspath('../../')

# Import Salt Libs
from salt.modules import s6

# Globals
s6.__salt__ = {}
s6.SERVICE_DIR = '/etc/service'


@skipIf(NO_MOCK, NO_MOCK_REASON)
class S6TestCase(TestCase):
'''
Test cases for salt.modules.s6
'''
# 'start' function tests: 1

def test_start(self):
'''
Test if it starts service via s6-svc.
'''
mock_ret = MagicMock(return_value=False)
with patch.dict(s6.__salt__, {'cmd.retcode': mock_ret}):
self.assertTrue(s6.start('ssh'))

# 'stop' function tests: 1

def test_stop(self):
'''
Test if it stops service via s6.
'''
mock_ret = MagicMock(return_value=False)
with patch.dict(s6.__salt__, {'cmd.retcode': mock_ret}):
self.assertTrue(s6.stop('ssh'))

# 'term' function tests: 1

def test_term(self):
'''
Test if it send a TERM to service via s6.
'''
mock_ret = MagicMock(return_value=False)
with patch.dict(s6.__salt__, {'cmd.retcode': mock_ret}):
self.assertTrue(s6.term('ssh'))

# 'reload_' function tests: 1

def test_reload(self):
'''
Test if it send a HUP to service via s6.
'''
mock_ret = MagicMock(return_value=False)
with patch.dict(s6.__salt__, {'cmd.retcode': mock_ret}):
self.assertTrue(s6.reload_('ssh'))

# 'restart' function tests: 1

def test_restart(self):
'''
Test if it restart service via s6. This will stop/start service.
'''
mock_ret = MagicMock(return_value=False)
with patch.dict(s6.__salt__, {'cmd.retcode': mock_ret}):
self.assertTrue(s6.restart('ssh'))

# 'full_restart' function tests: 1

def test_full_restart(self):
'''
Test if it calls s6.restart() function.
'''
mock_ret = MagicMock(return_value=False)
with patch.dict(s6.__salt__, {'cmd.retcode': mock_ret}):
self.assertIsNone(s6.full_restart('ssh'))

# 'status' function tests: 1

def test_status(self):
'''
Test if it return the status for a service via s6,
return pid if running.
'''
mock_run = MagicMock(return_value='salt')
with patch.dict(s6.__salt__, {'cmd.run_stdout': mock_run}):
self.assertEqual(s6.status('ssh'), '')

# 'available' function tests: 1

def test_available(self):
'''
Test if it returns ``True`` if the specified service is available,
otherwise returns ``False``.
'''
with patch.object(os, 'listdir',
MagicMock(return_value=['/etc/service'])):
self.assertTrue(s6.available('/etc/service'))

# 'missing' function tests: 1

def test_missing(self):
'''
Test if it returns ``True`` if the specified service is not available,
otherwise returns ``False``.
'''
with patch.object(os, 'listdir',
MagicMock(return_value=['/etc/service'])):
self.assertTrue(s6.missing('foo'))

# 'get_all' function tests: 1

def test_get_all(self):
'''
Test if it return a list of all available services.
'''
with patch.object(os, 'listdir',
MagicMock(return_value=['/etc/service'])):
self.assertListEqual(s6.get_all(), ['/etc/service'])


if __name__ == '__main__':
from integration import run_tests
run_tests(S6TestCase, needs_daemon=False)

0 comments on commit f735792

Please sign in to comment.