Merge branch '2015.5' of https://github.com/saltstack/salt into rabbitmq_vhost_states-unit-test

This commit is contained in:
Jayesh Kariya 2015-06-09 18:09:05 +05:30
commit 4bb6087722
54 changed files with 858 additions and 220 deletions

View File

@ -15,6 +15,14 @@ Description
The salt-call command is used to run module functions locally on a minion
instead of executing them from the master.
salt-call is used to run a :ref:`Standalone Minion <tutorial-standalone-minion>`,
and was originally created for :ref:`troubleshooting <troubleshooting-minion-salt-call>`.
Be aware that ``salt-call`` commands execute from the current user's shell
context, while ``salt`` commands execute from the system's default context.
By default, the Salt Master is contacted to retrieve state files and other resources
during execution unless the ``--local`` option is specified.:w
Options
=======

View File

@ -87,11 +87,6 @@ Execution Options
for this influx of vm creation. When creating large groups of VMs watch the
cloud provider carefully.
.. option:: -Q, --query
Execute a query and print out information about all cloud VMs. Can be used
in conjunction with -m to display only information about the specified map.
.. option:: -u, --update-bootstrap
Update salt-bootstrap to the latest develop version on GitHub.

View File

@ -2432,3 +2432,15 @@ List of git repositories to include with the local repo.
win_gitrepos:
- 'https://github.com/saltstack/salt-winrepo.git'
To specify a specific revision of the repository, preface the
repository location with a commit ID:
.. code-block:: yaml
win_gitrepos:
- '<commit_id> https://github.com/saltstack/salt-winrepo.git'
Replacing ``<commit_id>`` with the ID from GitHub. Specifying a commit
ID is useful if you need to revert to a previous version if an error
is introduced in the latest version.

View File

@ -289,6 +289,22 @@ executed. By default this feature is disabled, to enable set cache_jobs to
cache_jobs: False
.. conf_minion:: grains_cache
``grains_cache``
----------------
Default: ``False``
The minion can locally cache grain data instead of refreshing the data
each time the grain is referenced. By default this feature is disabled,
to enable set grains_cache to ``True``.
.. code-block:: yaml
cache_jobs: False
.. conf_minion:: sock_dir
``sock_dir``

View File

@ -24,6 +24,21 @@ The cp module is the home of minion side file server operations. The cp module
is used by the Salt state system, salt-cp, and can be used to distribute files
presented by the Salt file server.
Escaping Special Characters
```````````````````````````
The ``salt://`` url format can potentially contain a query string, for example
``salt://dir/file.txt?saltenv=base``. You can prevent the fileclient/fileserver from
interpreting ``?`` as the initial token of a query string by referencing the file
with ``salt://|`` rather than ``salt://``.
.. code-block:: yaml
/etc/marathon/conf/?checkpoint:
file.managed:
- source: salt://|hw/config/?checkpoint
- makedirs: True
Environments
````````````

View File

@ -3,4 +3,4 @@ salt.modules.ipmi
=================
.. automodule:: salt.modules.ipmi
:members
:members:

View File

@ -63,6 +63,7 @@ Full list of builtin state modules
influxdb_database
influxdb_user
ini_manage
ipmi
ipset
iptables
jboss7
@ -142,6 +143,7 @@ Full list of builtin state modules
sysrc
test
timezone
tls
tomcat
user
vbox_guest

View File

@ -0,0 +1,6 @@
================
salt.states.ipmi
================
.. automodule:: salt.states.ipmi
:members:

View File

@ -0,0 +1,6 @@
===============
salt.states.tls
===============
.. automodule:: salt.states.tls
:members:

View File

@ -173,6 +173,7 @@ Cloud Provider Specifics
Getting Started With Rackspace <rackspace>
Getting Started With SoftLayer <softlayer>
Getting Started With Vexxhost <vexxhost>
Getting Started With vSphere <vsphere>

View File

@ -0,0 +1,121 @@
============================
Getting Started With vSphere
============================
.. note::
.. deprecated:: Carbon
The :py:func:`vsphere <salt.cloud.clouds.vsphere>` cloud driver has been
deprecated in favor of the :py:func:`vmware <salt.cloud.clouds.vmware>`
cloud driver and will be removed in Salt Carbon. Please refer to
:doc:`Getting started with VMware </topics/cloud/vmware>` instead to get
started with the configuration.
VMware vSphere is a management platform for virtual infrastructure and cloud
computing.
Dependencies
============
The vSphere module for Salt Cloud requires the PySphere package, which is
available at PyPI:
https://pypi.python.org/pypi/pysphere
This package can be installed using `pip` or `easy_install`:
.. code-block:: bash
# pip install pysphere
# easy_install pysphere
Configuration
=============
Set up the cloud config at ``/etc/salt/cloud.providers`` or in the
``/etc/salt/cloud.providers.d/`` directory:
.. code-block:: yaml
my-vsphere-config:
provider: vsphere
# Set the vSphere access credentials
user: marco
password: polo
# Set the URL of your vSphere server
url: 'vsphere.example.com'
Profiles
========
Cloud Profiles
~~~~~~~~~~~~~~
vSphere uses a Managed Object Reference to identify objects located in vCenter.
The MOR ID's are used when configuring a vSphere cloud profile. Use the
following reference when locating the MOR's for the cloud profile.
http://kb.vmware.com/selfservice/microsites/search.do?cmd=displayKC&docType=kc&externalId=1017126&sliceId=1&docTypeID=DT_KB_1_1&dialogID=520386078&stateId=1%200%20520388386
Set up an initial profile at ``/etc/salt/cloud.profiles`` or in the
``/etc/salt/cloud.profiles.d`` directory:
.. code-block:: yaml
vsphere-centos:
provider: my-vsphere-config
image: centos
# Optional
datastore: datastore-15
resourcepool: resgroup-8
folder: salt-cloud
host: host-9
template: False
provider
--------
Enter the name that was specified when the cloud provider profile was created.
image
-----
Images available to build an instance can be found using the `--list-images`
option:
.. code-block:: bash
# salt-cloud --list-images my-vsphere-config
datastore
---------
The MOR of the datastore where the virtual machine should be located. If not
specified, the current datastore is used.
resourcepool
------------
The MOR of the resourcepool to be used for the new vm. If not set, it will use
the same resourcepool as the original vm.
folder
------
Name of the folder that will contain the new VM. If not set, the VM will be
added to the folder the original VM belongs to.
host
----
The MOR of the host where the vm should be registered.
If not specified:
* if resourcepool is not specified, the current host is used.
* if resourcepool is specified, and the target pool represents a
stand-alone host, the host is used.
* if resourcepool is specified, and the target pool represents a
DRS-enabled cluster, a host selected by DRS is used.
* if resourcepool is specified, and the target pool represents a
cluster without DRS enabled, an InvalidArgument exception will be thrown.
template
--------
Specifies whether or not the new virtual machine should be marked as a
template. Default is False.

View File

@ -1,6 +1,8 @@
===================
.. _external-pillars:
================
External Pillars
===================
================
Salt provides a mechanism for generating pillar data by calling external
pillar interfaces. This document will describe an outline of an ext_pillar

View File

@ -43,8 +43,9 @@ minion exe>` should match the contents of the corresponding md5 file.
* 2014.7.0
* Salt-Minion-2014.7.0-1-win32-Setup.exe | md5
* Salt-Minion-2014.7.0-AMD64-Setup.exe | md5
.. note::
The 2014.7.0 installers have been removed because of a regression. Please use the 2014.7.1 release instead.
.. note::
The 2014.7.0 installers have been removed because of a regression. Please use the 2014.7.1 release instead.
* 2014.1.13
* `Salt-Minion-2014.1.13-x86-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2014.1.13-x86-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2014.1.13-x86-Setup.exe.md5>`__

View File

@ -66,6 +66,8 @@ check that no additional access control system such as `SELinux`_ or
.. _`SELinux`: https://en.wikipedia.org/wiki/Security-Enhanced_Linux
.. _`AppArmor`: http://wiki.apparmor.net/index.php/Main_Page
.. _troubleshooting-minion-salt-call:
Using salt-call
===============

View File

@ -34,9 +34,9 @@ Variables:
minions can be defined in pillar and then accessed inside sls formulas
and template files.
Arbitrary Data:
Pillar can contain any basic data structure, so a list of values, or a
key/value store can be defined making it easy to iterate over a group
of values in sls formulas
Pillar can contain any basic data structure in dictionary format,
so a key/value store can be defined making it easy to iterate over a group
of values in sls formulas.
Pillar is therefore one of the most important systems when using Salt. This
walkthrough is designed to get a simple Pillar up and running in a few minutes
@ -309,12 +309,6 @@ line:
salt '*' state.sls my_sls_file pillar='{"hello": "world"}'
Lists can be passed in pillar as well:
.. code-block:: bash
salt '*' state.highstate pillar='["foo", "bar", "baz"]'
.. note::
If a key is passed on the command line that already exists on the minion,

View File

@ -1,3 +1,5 @@
.. _tutorial-standalone-minion:
=================
Standalone Minion
=================
@ -77,4 +79,10 @@ it unnecessary to change the configuration file:
.. code-block:: bash
salt-call state.highstate --local
salt-call state.highstate --local
External Pillars
================
:ref:`External pillars <external-pillars>` are supported when running in masterless mode.

View File

@ -178,8 +178,8 @@ and all changes made.
salt-minion -l debug
Run the minion in the foreground
By not starting the minion in daemon mode (:option:`-d <salt-minion
-d>`) one can view any output from the minion as it works:
By not starting the minion in daemon mode (:option:`-d <salt-minion -d>`)
one can view any output from the minion as it works:
.. code-block:: bash

View File

@ -9,29 +9,29 @@ use an existing table that has a username and a password column.
To get started, create a simple table that holds just a username and
a password. The password field will hold a SHA256 checksum.
.. code-block:: sql
.. code-block:: sql
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(25) DEFAULT NULL,
`password` varchar(70) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(25) DEFAULT NULL,
`password` varchar(70) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
To create a user within MySQL, execute the following statement.
.. code-block:: sql
.. code-block:: sql
INSERT INTO users VALUES (NULL, 'diana', SHA2('secret', 256))
INSERT INTO users VALUES (NULL, 'diana', SHA2('secret', 256))
.. code-block:: yaml
.. code-block:: yaml
mysql_auth:
hostname: localhost
database: SaltStack
username: root
password: letmein
auth_sql: 'SELECT username FROM users WHERE username = "{0}" AND password = SHA2("{1}", 256)'
mysql_auth:
hostname: localhost
database: SaltStack
username: root
password: letmein
auth_sql: 'SELECT username FROM users WHERE username = "{0}" AND password = SHA2("{1}", 256)'
The `auth_sql` contains the SQL that will validate a user to ensure they are
correctly authenticated. This is where you can specify other SQL queries to
@ -39,7 +39,7 @@ authenticate users.
Enable MySQL authentication.
.. code-block:: yaml
.. code-block:: yaml
external_auth:
mysql:

View File

@ -84,24 +84,26 @@ def beacon(config):
The mask list can contain the following events (the default mask is create,
delete, and modify):
* access File accessed
* attrib File metadata changed
* close_nowrite Unwritable file closed
* close_write Writable file closed
* create File created in watched directory
* delete File deleted from watched directory
* delete_self Watched file or directory deleted
* modify File modified
* moved_from File moved out of watched directory
* moved_to File moved into watched directory
* move_self Watched file moved
* open File opened
* access File accessed
* attrib File metadata changed
* close_nowrite Unwritable file closed
* close_write Writable file closed
* create File created in watched directory
* delete File deleted from watched directory
* delete_self Watched file or directory deleted
* modify File modified
* moved_from File moved out of watched directory
* moved_to File moved into watched directory
* move_self Watched file moved
* open File opened
The mask can also contain the following options:
* dont_follow Don't dereference symbolic links
* excl_unlink Omit events for children after they have been unlinked
* oneshot Remove watch after one event
* onlydir Operate only if name is directory
* dont_follow Don't dereference symbolic links
* excl_unlink Omit events for children after they have been unlinked
* oneshot Remove watch after one event
* onlydir Operate only if name is directory
recurse:
Recursively watch files in the directory

View File

@ -139,10 +139,10 @@ class Batch(object):
for ping_ret in self.ping_gen:
if ping_ret is None:
break
if ping_ret not in self.minions:
self.minions.append(ping_ret)
to_run.append(ping_ret)
m = next(ping_ret.iterkeys())
if m not in self.minions:
self.minions.append(m)
to_run.append(m)
for queue in iters:
try:
# Gather returns until we get to the bottom

View File

@ -13,6 +13,7 @@ class SaltSSH(parsers.SaltSSHOptionParser):
def run(self):
self.parse_args()
self.setup_logfile_logger()
ssh = salt.client.ssh.SSH(self.config)
ssh.run()

View File

@ -3,7 +3,15 @@
vSphere Cloud Module
====================
.. versionadded:: 2014.7.0
.. note::
.. deprecated:: Carbon
The :py:func:`vsphere <salt.cloud.clouds.vsphere>` cloud driver has been
deprecated in favor of the :py:func:`vmware <salt.cloud.clouds.vmware>`
cloud driver and will be removed in Salt Carbon. Please refer to
:doc:`Getting started with VMware </topics/cloud/vmware>` to get started
and convert your vsphere provider configurations to use the vmware driver.
The vSphere cloud module is used to control access to VMWare vSphere.
@ -76,6 +84,7 @@ import time
import salt.utils.cloud
import salt.utils.xmlutil
from salt.exceptions import SaltCloudSystemExit
from salt.utils import warn_until
# Import salt cloud libs
import salt.config as config
@ -110,6 +119,11 @@ def get_configured_provider():
'''
Return the first configured instance.
'''
warn_until(
'Carbon',
'The vsphere driver is deprecated in favor of the vmware driver and will be removed '
'in Salt Carbon. Please convert your vsphere provider configs to use the vmware driver.'
)
return config.is_provider_configured(
__opts__,
__active_provider_name__ or 'vsphere',

View File

@ -795,7 +795,8 @@ class Minion(MinionBase):
' {0}'.format(opts['master']))
if opts['master_shuffle']:
shuffle(opts['master'])
elif isinstance(opts['master'], str):
# if opts['master'] is a str and we have never created opts['master_list']
elif isinstance(opts['master'], str) and ('master_list' not in opts):
# We have a string, but a list was what was intended. Convert.
# See issue 23611 for details
opts['master'] = list(opts['master'])

View File

@ -1249,13 +1249,18 @@ def get_repo(repo, **kwargs):
ppa_name, dist)
else:
if HAS_SOFTWAREPROPERTIES:
if hasattr(softwareproperties.ppa, 'PPAShortcutHandler'):
repo = softwareproperties.ppa.PPAShortcutHandler(repo).expand(
__grains__['lsb_distrib_codename'])[0]
else:
repo = softwareproperties.ppa.expand_ppa_line(
repo,
__grains__['lsb_distrib_codename'])[0]
try:
if hasattr(softwareproperties.ppa, 'PPAShortcutHandler'):
repo = softwareproperties.ppa.PPAShortcutHandler(
repo).expand(dist)[0]
else:
repo = softwareproperties.ppa.expand_ppa_line(
repo,
dist)[0]
except NameError as name_error:
raise CommandExecutionError(
'Could not find ppa {0}: {1}'.format(repo, name_error)
)
else:
repo = LP_SRC_FORMAT.format(owner_name, ppa_name, dist)

View File

@ -647,6 +647,7 @@ def list_permissions(username=None, resource=None, resource_type='keyspace', per
:rtype: dict
.. code-block:: bash
salt 'minion1' cassandra_cql.list_permissions
salt 'minion1' cassandra_cql.list_permissions username=joe resource=test_keyspace permission=select
@ -702,6 +703,7 @@ def grant_permission(username, resource=None, resource_type='keyspace', permissi
:rtype:
.. code-block:: bash
salt 'minion1' cassandra_cql.grant_permission
salt 'minion1' cassandra_cql.grant_permission username=joe resource=test_keyspace permission=select

View File

@ -579,18 +579,18 @@ def run(cmd,
Note that ``env`` represents the environment variables for the command, and
should be formatted as a dict, or a YAML string which resolves to a dict.
*************************************************************************
WARNING: This function does not process commands through a shell
unless the python_shell flag is set to True. This means that any
shell-specific functionality such as 'echo' or the use of pipes,
redirection or &&, should either be migrated to cmd.shell or
have the python_shell=True flag set here.
.. warning::
The use of python_shell=True means that the shell will accept _any_ input
including potentially malicious commands such as 'good_command;rm -rf /'.
Be absolutely certain that you have sanitized your input prior to using
python_shell=True
*************************************************************************
This function does not process commands through a shell
unless the python_shell flag is set to True. This means that any
shell-specific functionality such as 'echo' or the use of pipes,
redirection or &&, should either be migrated to cmd.shell or
have the python_shell=True flag set here.
The use of python_shell=True means that the shell will accept _any_ input
including potentially malicious commands such as 'good_command;rm -rf /'.
Be absolutely certain that you have sanitized your input prior to using
python_shell=True
CLI Example:
@ -707,12 +707,12 @@ def shell(cmd,
.. versionadded:: 2015.5.0
************************************************************
WARNING: This passes the cmd argument directly to the shell
without any further processing! Be absolutely sure that you
have properly santized the command passed to this function
and do not use untrusted inputs.
************************************************************
.. warning ::
This passes the cmd argument directly to the shell
without any further processing! Be absolutely sure that you
have properly santized the command passed to this function
and do not use untrusted inputs.
Note that ``env`` represents the environment variables for the command, and
should be formatted as a dict, or a YAML string which resolves to a dict.

View File

@ -563,7 +563,8 @@ def create_container(image,
volumes_from=None,
name=None,
cpu_shares=None,
cpuset=None):
cpuset=None,
binds=None):
'''
Create a new container
@ -582,10 +583,26 @@ def create_container(image,
ports
port redirections ``({'222': {}})``
volumes
list of volume mappings::
list of volume mappings in either local volume, bound volume, or read-only
bound volume form::
(['/mountpoint/in/container:/guest/foo', '/same/path/mounted/point'])
(['/var/lib/mysql/', '/usr/local/etc/ssl:/etc/ssl', '/etc/passwd:/etc/passwd:ro'])
binds
complete dictionary of bound volume mappings::
{ '/usr/local/etc/ssl/certs/internal.crt': {
'bind': '/etc/ssl/certs/com.example.internal.crt',
'ro': True
},
'/var/lib/mysql': {
'bind': '/var/lib/mysql/',
'ro': False
}
}
This dictionary is suitable for feeding directly into the Docker API, and all
keys are required.
(see http://docker-py.readthedocs.org/en/latest/volumes/)
tty
attach ttys, Default is ``False``
stdin_open
@ -604,23 +621,31 @@ def create_container(image,
salt '*' docker.create_container o/ubuntu volumes="['/s','/m:/f']"
'''
log.trace("modules.dockerio.create_container() called for image " + image)
status = base_status.copy()
client = _get_client()
# In order to permit specification of bind volumes in the volumes field,
# we'll look through it for bind-style specs and move them. This is purely
# for CLI convenience and backwards-compatibility, as states.dockerio
# should parse volumes before this, and the binds argument duplicates this.
# N.B. this duplicates code in states.dockerio._parse_volumes()
if isinstance(volumes, list):
for volume in volumes:
if ':' in volume:
volspec = volume.split(':')
source = volspec[0]
target = volspec[1]
ro = False
try:
if len(volspec) > 2:
ro = volspec[2] == "ro"
except IndexError:
pass
binds[source] = {'bind': target, 'ro': ro}
volumes.remove(volume)
try:
mountpoints = {}
binds = {}
# create empty mountpoints for them to be
# editable
# either we have a list of guest or host:guest
if isinstance(volumes, list):
for mountpoint in volumes:
mounted = mountpoint
if ':' in mountpoint:
parts = mountpoint.split(':')
mountpoint = parts[1]
mounted = parts[0]
mountpoints[mountpoint] = {}
binds[mounted] = mountpoint
container_info = client.create_container(
image=image,
command=command,
@ -633,12 +658,14 @@ def create_container(image,
ports=ports,
environment=environment,
dns=dns,
volumes=mountpoints,
volumes=volumes,
volumes_from=volumes_from,
name=name,
cpu_shares=cpu_shares,
cpuset=cpuset
cpuset=cpuset,
host_config=docker.utils.create_host_config(binds=binds)
)
log.trace("docker.client.create_container returned: " + str(container_info))
container = container_info['Id']
callback = _valid
comment = 'Container created'
@ -648,8 +675,9 @@ def create_container(image,
}
__salt__['mine.send']('docker.get_containers', host=True)
return callback(status, id_=container, comment=comment, out=out)
except Exception:
except Exception, e:
_invalid(status, id_=image, out=traceback.format_exc())
raise e
__salt__['mine.send']('docker.get_containers', host=True)
return status
@ -1807,7 +1835,7 @@ def _run_wrapper(status, container, func, cmd, *args, **kwargs):
def load(imagepath):
'''
Load the specified file at imagepath into docker that was generated from
a docker save command
a docker save command
e.g. `docker load < imagepath`
imagepath

View File

@ -590,9 +590,7 @@ def import_key(user=None,
.. code-block:: bash
salt '*' gpg.import_key text='-----BEGIN PGP PUBLIC KEY BLOCK-----\n ... -----END PGP PUBLIC KEY BLOCK-----'
salt '*' gpg.import_key filename='/path/to/public-key-file'
'''
ret = {
'res': True,
@ -749,7 +747,6 @@ def trust_key(keyid=None,
salt '*' gpg.trust_key fingerprint='53C96788253E58416D20BCD352952C84C3252192' trust_level='not_trusted'
salt '*' gpg.trust_key keys=3FAD9F1E trust_level='ultimately' user='username'
'''
ret = {
'res': True,

View File

@ -123,16 +123,18 @@ def shadow_hash(crypt_salt=None, password=None, algorithm='sha512'):
def rand_int(start=1, end=10):
'''
.. versionadded: 2015.5.3
Returns a random integer number between the start and end number.
.. versionadded: 2015.5.3
start : 1
Any valid integer number
end : 10
Any valid integer number
CLI Example:
.. code-block:: bash
salt '*' random.rand_int 1 10

View File

@ -687,7 +687,7 @@ def mount(name, device, mkmnt=False, fstype='', opts='defaults', user=None):
opts = opts.split(',')
if not os.path.exists(name) and mkmnt:
__salt__['file.mkdir'](name=name, user=user)
__salt__['file.mkdir'](name, user=user)
args = ''
if opts is not None:

View File

@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
'''
:requires: libnacl
https://github.com/saltstack/libnacl
This module helps include encrypted passwords in pillars, grains and salt state files.
This is often usefull if you wish to store your pillars in source control or
share your pillar data with others that you trust. I dont advise making your pillars public
:depends: libnacl, https://github.com/saltstack/libnacl
This is often useful if you wish to store your pillars in source control or
share your pillar data with others that you trust. I don't advise making your pillars public
regardless if they are encrypted or not.
When generating keys and encrypting passwords use --local when using salt-call for extra
@ -14,20 +14,28 @@ security. Also consider using just the salt runner nacl when encrypting pillar p
The nacl lib uses 32byte keys, these keys are base64 encoded to make your life more simple.
To generate your `key` or `keyfile` you can use:
.. code-block:: bash
salt-call --local nacl.keygen keyfile=/root/.nacl
Now with your key, you can encrypt some data
Now with your key, you can encrypt some data:
.. code-block:: bash
salt-call --local nacl.enc mypass keyfile=/root/.nacl
DRB7Q6/X5gGSRCTpZyxS6hXO5LnlJIIJ4ivbmUlbWj0llUA+uaVyvou3vJ4=
To decrypt the data
To decrypt the data:
.. code-block:: bash
salt-call --local nacl.dec data='DRB7Q6/X5gGSRCTpZyxS6hXO5LnlJIIJ4ivbmUlbWj0llUA+uaVyvou3vJ4=' keyfile=/root/.nacl
mypass
The following optional configurations can be defined in the
minion or master config. Avoide storeing the config in pillars!
minion or master config. Avoid storing the config in pillars!
.. code-block:: yaml
cat /etc/salt/master.d/nacl.conf
nacl.config:
@ -36,15 +44,21 @@ minion or master config. Avoide storeing the config in pillars!
When the key is defined in the master config you can use it from the nacl runner:
.. code-block:: bash
salt-run nacl.enc 'myotherpass'
Now you can create a pillar with protected data like:
.. code-block:: yaml
pillarexample:
user: root
password: {{ salt.nacl.dec('DRB7Q6/X5gGSRCTpZyxS6hXO5LnlJIIJ4ivbmUlbWj0llUA+uaVyvou3vJ4=') }}
Or do somthing interesting with grains like:
Or do something interesting with grains like:
.. code-block:: yaml
salt-call nacl.enc minionname:dbrole
AL24Z2C5OlkReer3DuQTFdrNLchLuz3NGIhGjZkLtKRYry/b/CksWM8O9yskLwH2AGVLoEXI5jAa

View File

@ -119,6 +119,11 @@ def install(pkg=None,
elif pkgs:
cmd += ' "{0}"'.format('" "'.join(pkgs))
if runas:
uid = salt.utils.get_uid(runas)
if uid:
env.update({'SUDO_UID': uid, 'SUDO_USER': ''})
result = __salt__['cmd.run_all'](cmd, python_shell=False, cwd=dir, runas=runas, env=env)
if result['retcode'] != 0:
@ -151,7 +156,8 @@ def install(pkg=None,
def uninstall(pkg,
dir=None,
runas=None):
runas=None,
env=None):
'''
Uninstall an NPM package.
@ -167,6 +173,13 @@ def uninstall(pkg,
runas
The user to run NPM with
env
Environment variables to set when invoking npm. Uses the same ``env``
format as the :py:func:`cmd.run <salt.modules.cmdmod.run>` execution
function.
.. versionadded:: 2015.5.3
CLI Example:
.. code-block:: bash
@ -175,6 +188,11 @@ def uninstall(pkg,
'''
if runas:
uid = salt.utils.get_uid(runas)
if uid:
env.update({'SUDO_UID': uid, 'SUDO_USER': ''})
cmd = 'npm uninstall'
if dir is None:
@ -182,7 +200,7 @@ def uninstall(pkg,
cmd += ' "{0}"'.format(pkg)
result = __salt__['cmd.run_all'](cmd, python_shell=False, cwd=dir, runas=runas)
result = __salt__['cmd.run_all'](cmd, python_shell=False, cwd=dir, runas=runas, env=env)
if result['retcode'] != 0:
log.error(result['stderr'])
@ -227,6 +245,11 @@ def list_(pkg=None,
'''
if runas:
uid = salt.utils.get_uid(runas)
if uid:
env.update({'SUDO_UID': uid, 'SUDO_USER': ''})
cmd = 'npm list --silent --json'
if dir is None:

View File

@ -225,11 +225,13 @@ def enable():
def disable(message=None):
'''
.. versionadded:: 2014.7.0
Disable the puppet agent
message
.. versionadded:: 2015.5.2
disable message to send to puppet
Disable message to send to puppet
CLI Example:
@ -237,7 +239,6 @@ def disable(message=None):
salt '*' puppet.disable
salt '*' puppet.disable 'disabled for a good reason'
'''
_check_puppet()

View File

@ -681,10 +681,9 @@ def generateBlobs(api_key=None,
.. code-block:: bash
salt '*' get_intergers number=5 min=1 max=6
salt '*' get_intergers number=5 min=1 max=6
salt '*' get_integers number=5 min=1 max=6
salt '*' get_integers number=5 min=1 max=6
'''
ret = {'res': True}

View File

@ -1,6 +1,12 @@
# -*- coding: utf-8 -*-
'''
Control the state system on the minion
Control the state system on the minion.
State Caching
-------------
When a highstate is called, the minion automatically caches a copy of the last high data.
If you then run a highstate with cache=True it will use that cached highdata and won't hit the fileserver.
'''
from __future__ import absolute_import

View File

@ -52,8 +52,7 @@ def _call_system_profiler(datatype):
def receipts():
'''
Return the results of a call to
`system_profiler -xml -detail full
SPInstallHistoryDataType`
``system_profiler -xml -detail full SPInstallHistoryDataType``
as a dictionary. Top-level keys of the dictionary
are the names of each set of install receipts, since
there can be multiple receipts with the same name.
@ -93,12 +92,11 @@ def receipts():
def applications():
'''
Return the results of a call to
`system_profiler -xml -detail full
SPApplicationsDataType`
``system_profiler -xml -detail full SPApplicationsDataType``
as a dictionary. Top-level keys of the dictionary
are the names of each set of install receipts, since
there can be multiple receipts with the same name.
Contents of each key are a list of dicttionaries.
Contents of each key are a list of dictionaries.
Note that this can take a long time depending on how many
applications are installed on the target Mac.

View File

@ -7,16 +7,18 @@ A REST API for Salt
.. py:currentmodule:: salt.netapi.rest_cherrypy.app
:depends: - CherryPy Python module. Versions 3.2.{2,3,4} are strongly
recommended due to a known `SSL error
<https://bitbucket.org/cherrypy/cherrypy/issue/1298/ssl-not-working>`_
introduced in version 3.2.5. The issue was reportedly resolved with
CherryPy milestone 3.3, but the patch was committed for version 3.6.1.
:depends:
- CherryPy Python module. Versions 3.2.{2,3,4} are strongly
recommended due to a known `SSL error
<https://bitbucket.org/cherrypy/cherrypy/issue/1298/ssl-not-working>`_
introduced in version 3.2.5. The issue was reportedly resolved with
CherryPy milestone 3.3, but the patch was committed for version 3.6.1.
:optdepends: - ws4py Python module for websockets support.
:client_libraries:
- Java: https://github.com/SUSE/saltstack-netapi-client-java
- Python: https://github.com/saltstack/pepper
:configuration: All authentication is done through Salt's :ref:`external auth
:configuration:
All authentication is done through Salt's :ref:`external auth
<acl-eauth>` system which requires additional configuration not described
here.

View File

@ -16,7 +16,7 @@ them.
An example Django module that registers a function called
'returner_callback' with this module's 'returner' function:
.. code-block:: python
.. code-block:: python
import salt.returners.django_return
from django.dispatch import receiver

View File

@ -84,6 +84,7 @@ There is also the option of specifying a dynamic inventory, and generating it on
This is the format that an inventory script needs to output to work with ansible, and thus here.
.. code-block:: bash
[~]# salt-ssh --roster-file /etc/salt/hosts salt.gtmanfred.com test.ping
salt.gtmanfred.com:
True

View File

@ -349,7 +349,7 @@ def lock(backend=None, remote=None):
.. note::
This will only operate on enabled backends (those configured in
:master_conf:`fileserver_backend`).
:conf_master:`fileserver_backend`).
backend
Only set the update lock for the specified backend(s).

View File

@ -19,7 +19,7 @@ def query(url, output=True, **kwargs):
'''
Query a resource, and decode the return data
.. versionaddedd:: 2015.2
.. versionadded:: 2015.5.0
CLI Example:
@ -44,7 +44,7 @@ def update_ca_bundle(target=None, source=None, merge_files=None):
'''
Update the local CA bundle file from a URL
.. versionaddedd:: 2015.2
.. versionadded:: 2015.5.0
CLI Example:

View File

@ -234,10 +234,10 @@ def present(
listeners
A list of listener lists; example:
[
['443', 'HTTPS', 'arn:aws:iam::1111111:server-certificate/mycert'],
['8443', '80', 'HTTPS', 'HTTP', 'arn:aws:iam::1111111:server-certificate/mycert']
]
[
['443', 'HTTPS', 'arn:aws:iam::1111111:server-certificate/mycert'],
['8443', '80', 'HTTPS', 'HTTP', 'arn:aws:iam::1111111:server-certificate/mycert']
]
subnets
A list of subnet IDs in your VPC to attach to your LoadBalancer.

View File

@ -74,6 +74,7 @@ Available Functions
my_service:
docker.running:
- container: mysuperdocker
- image: corp/mysuperdocker_img
- port_bindings:
"5000/tcp":
HostIp: ""
@ -184,6 +185,103 @@ def _invalid(exec_status=None, name='', comment='', changes=None):
result=False)
def _parse_volumes(volumes):
'''
Parse a given volumes state specification for later use in
modules.docker.create_container(). This produces a dict that can be directly
consumed by the Docker API /containers/create.
Note: this only really exists for backwards-compatibility, and because
modules.dockerio.start() currently takes a binds argument.
volumes
A structure containing information about the volumes to be included in the
container that will be created, either:
- a bare dictionary
- a list of dictionaries and lists
.. code-block:: yaml
# bare dict style
- volumes:
/usr/local/etc/ssl/certs/example.crt:
bind: /etc/ssl/certs/com.example.internal.crt
ro: True
/var/run:
bind: /var/run/host/
ro: False
# list of dicts style:
- volumes:
- /usr/local/etc/ssl/certs/example.crt:
bind: /etc/ssl/certs/com.example.internal.crt
ro: True
- /var/run: /var/run/host/ # read-write bound volume
- /var/lib/mysql # un-bound, container-only volume
note: bind mounts specified like "/etc/timezone:/tmp/host_tz" will fall
through this parser.
Returns a dict of volume specifications:
.. code-block:: yaml
{
'bindvols': {
'/usr/local/etc/ssl/certs/example.crt': {
'bind': '/etc/ssl/certs/com.example.internal.crt',
'ro': True
},
'/var/run/': {
'bind': '/var/run/host',
'ro': False
},
},
'contvols': [ '/var/lib/mysql/' ]
}
'''
log.trace("Parsing given volumes dict: " + str(volumes))
bindvolumes = {}
contvolumes = []
if isinstance(volumes, dict):
# If volumes as a whole is a dict, then there's no way to specify a non-bound volume
# so we exit early and assume the dict is properly formed.
bindvolumes = volumes
if isinstance(volumes, list):
for vol in volumes:
if isinstance(vol, dict):
for volsource, voldef in vol.items():
if isinstance(voldef, dict):
target = voldef['bind']
read_only = voldef.get('ro', False)
else:
target = str(voldef)
read_only = False
source = volsource
else: # isinstance(vol, dict)
if ':' in vol:
volspec = vol.split(':')
source = volspec[0]
target = volspec[1]
read_only = False
try:
if len(volspec) > 2:
read_only = volspec[2] == "ro"
except IndexError:
pass
else:
contvolumes.append(str(vol))
continue
bindvolumes[source] = {
'bind': target,
'ro': read_only
}
result = {'bindvols': bindvolumes, 'contvols': contvolumes}
log.trace("Finished parsing volumes, with result: " + str(result))
return result
def mod_watch(name, sfun=None, *args, **kw):
if sfun == 'built':
# Needs to refresh the image
@ -479,7 +577,7 @@ def installed(name,
- a port to map
- a mapping of mapping portInHost : PortInContainer
volumes
List of volumes
List of volumes (see notes for the running function)
For other parameters, see absolutely first the salt.modules.dockerio
execution module and the docker-py python bindings for docker
@ -502,7 +600,7 @@ def installed(name,
# if container exists but is not started, try to start it
if already_exists:
return _valid(comment='image {0!r} already exists'.format(name))
dports, dvolumes, denvironment = {}, [], {}
dports, denvironment = {}, {}
if not ports:
ports = []
if not volumes:
@ -521,15 +619,13 @@ def installed(name,
else:
for k in p:
dports[str(p)] = {}
for p in volumes:
vals = []
if not isinstance(p, dict):
vals.append('{0}'.format(p))
else:
for k in p:
vals.append('{0}:{1}'.format(k, p[k]))
dvolumes.extend(vals)
parsed_volumes = _parse_volumes(volumes)
bindvolumes = parsed_volumes['bindvols']
contvolumes = parsed_volumes['contvols']
a, kw = [image], dict(
binds=bindvolumes,
command=command,
hostname=hostname,
user=user,
@ -540,7 +636,7 @@ def installed(name,
ports=dports,
environment=denvironment,
dns=dns,
volumes=dvolumes,
volumes=contvolumes,
volumes_from=volumes_from,
name=name,
cpu_shares=cpu_shares,
@ -799,44 +895,47 @@ def running(name,
volumes
List of volumes to mount or create in the container (like ``-v`` of ``docker run`` command),
mapping host directory to container directory.
To create a volume in the container:
To specify a volume in the container in terse list format:
.. code-block:: yaml
- volumes:
- "/var/log/service"
- "/var/log/service" # container-only volume
- "/srv/timezone:/etc/timezone" # bound volume
- "/usr/local/etc/passwd:/etc/passwd:ro" # read-only bound volume
For read-write mounting, use the short form (note that the notion of
You can also use the short dictionary form (note that the notion of
source:target from docker is preserved):
.. code-block:: yaml
- volumes:
- /var/log/service: /var/log/service
- /var/log/service: /var/log/service # mandatory read-write implied
Or, to specify read-only mounting, use the extended form:
Or, alternatively, to specify read-only mounting, use the extended form:
.. code-block:: yaml
- volumes:
- /home/user1:
bind: /mnt/vol2
ro: true
bind: /mnt/vol2
ro: True
- /var/www:
bind: /mnt/vol1
ro: false
bind: /mnt/vol1
ro: False
Or (mostly for backwards compatibility) a dict style
Or (for backwards compatibility) another dict style:
.. code-block:: yaml
- volumes:
/home/user1:
bind: /mnt/vol2
ro: true
/var/www:
bind: /mnt/vol1
ro: false
/home/user1:
bind: /mnt/vol2
ro: True
/var/www:
bind: /mnt/vol1
ro: False
volumes_from
List of containers to share volumes with
@ -934,27 +1033,6 @@ def running(name,
if isinstance(var, dict):
for key in var:
denvironment[six.text_type(key)] = six.text_type(var[key])
if isinstance(volumes, dict):
bindvolumes = volumes
if isinstance(volumes, list):
for vol in volumes:
if isinstance(vol, dict):
# get source as the dict key
source = list(vol.keys())[0]
# then find target
if isinstance(vol[source], dict):
target = vol[source]['bind']
read_only = vol[source].get('ro', False)
else:
target = str(vol[source])
read_only = False
bindvolumes[source] = {
'bind': target,
'ro': read_only
}
else:
# assume just an own volumes
contvolumes.append(str(vol))
if isinstance(ports, dict):
bindports = ports
# in dict form all ports bind, so no need for exposeports
@ -976,6 +1054,11 @@ def running(name,
else:
#assume just a port to expose
exposeports.append(str(port))
parsed_volumes = _parse_volumes(volumes)
bindvolumes = parsed_volumes['bindvols']
contvolumes = parsed_volumes['contvols']
if not already_exists:
args, kwargs = [image], dict(
command=command,
@ -988,6 +1071,7 @@ def running(name,
ports=exposeports,
environment=denvironment,
dns=dns,
binds=bindvolumes,
volumes=contvolumes,
name=name,
cpu_shares=cpu_shares,

View File

@ -164,7 +164,7 @@ elasticsearch profile via pillars:
The behavior of this module is to create dashboards if they do not exist, to
add rows if they do not exist in existing dashboards, and to update rows if
they exist in dashboards. The module will not manage rows that are not defined,
allowing users to manage their own custom rows.
allowing users to manage their own custom rows.
'''
from __future__ import absolute_import
from salt.exceptions import SaltInvocationError

View File

@ -73,6 +73,7 @@ def send_message(name,
The message that is to be sent to the Hipchat room.
The following parameters are optional:
api_key
The api key for Hipchat to use for authentication,
if not specified in the configuration options of master or minion.

View File

@ -163,8 +163,10 @@ def user_present(name, uid, password, channel=14, callback=False,
callback
User Restricted to Callback
False = User Privilege Limit is determined by the User Privilege Limit
parameter privilege_level, for both callback and non-callback connections.
True = User Privilege Limit is determined by the privilege_level
parameter for callback connections, but is restricted to Callback
level for non-callback connections. Thus, a user can only initiate

View File

@ -4,7 +4,8 @@ Linux File Access Control Lists
Ensure a Linux ACL is present
.. code-block:: yaml
.. code-block:: yaml
root:
acl.present:
- name: /root
@ -14,7 +15,8 @@ Ensure a Linux ACL is present
Ensure a Linux ACL does not exist
.. code-block:: yaml
.. code-block:: yaml
root:
acl.absent:
- name: /root

View File

@ -19,7 +19,7 @@ def license_present(name):
on the host.
name
The licnese key to ensure is present
The license key to ensure is present
'''
ret = {'name': name,
'changes': {},
@ -61,7 +61,7 @@ def license_absent(name):
on the host.
name
The licnese key to ensure is absent
The license key to ensure is absent
'''
ret = {'name': name,
'changes': {},

View File

@ -66,10 +66,10 @@ def post_message(name,
The message that is to be sent to the Hipchat room.
The following parameters are optional:
api_key
The api key for Slack to use for authentication,
if not specified in the configuration options of master or minion.
'''
ret = {'name': name,
'changes': {},

View File

@ -73,23 +73,26 @@ def installed(name, recurse=False, force=False):
def removed(name):
'''
Remove the windows feature
name:
short name of the feature (the right column in win_servermanager.list_available)
Note:
.. note::
Some features require a reboot after uninstallation. If so the feature will not be completly uninstalled until
the server is restarted.
the server is restarted.
Example:
Run ``salt MinionName win_servermanager.list_installed`` to get a list of all features installed. Use the top
Run ``salt MinionName win_servermanager.list_installed`` to get a list of all features installed. Use the top
name listed for each feature, not the indented one. Do not use the role or feature names mentioned in the
PKGMGR documentation.
.. code-block:: yaml
ISWebserverRole:
win_servermanager.removed:
- name: Web-Server
.. code-block:: yaml
ISWebserverRole:
win_servermanager.removed:
- name: Web-Server
'''
ret = {'name': name,
'result': True,

View File

@ -30,6 +30,12 @@ try:
except ImportError:
# Older jinja does not need markupsafe
HAS_MARKUPSAFE = False
try:
import xml
HAS_XML = True
except ImportError:
HAS_XML = False
# pylint: enable=import-error,no-name-in-module
try:
@ -115,6 +121,10 @@ def gen_thin(cachedir, extra_mods='', overwrite=False, so_mods=''):
if HAS_SSL_MATCH_HOSTNAME:
tops.append(os.path.dirname(os.path.dirname(ssl_match_hostname.__file__)))
if HAS_XML:
# For openSUSE, which apparently doesn't include the whole stdlib
tops.append(os.path.dirname(xml.__file__))
for mod in [m for m in extra_mods.split(',') if m]:
if mod not in locals() and mod not in globals():
try:

View File

@ -52,19 +52,22 @@ def update_config(file_name, yaml_contents):
specified by ``default_include``.
This folder is named ``master.d`` by
default. Please look at
http://docs.saltstack.com/en/latest/ref/configuration/master.html#include-configuration
:conf_master:`include-configuration`
for more information.
Example low data::
data = {
'username': 'salt',
'password': 'salt',
'fun': 'config.update_config',
'file_name': 'gui',
'yaml_contents': {'id': 1},
'client': 'wheel',
'eauth': 'pam',
}
Example low data:
.. code-block:: yaml
data = {
'username': 'salt',
'password': 'salt',
'fun': 'config.update_config',
'file_name': 'gui',
'yaml_contents': {'id': 1},
'client': 'wheel',
'eauth': 'pam',
}
'''
file_name = '{0}{1}'.format(file_name, '.conf')
dir_path = os.path.join(__opts__['config_dir'],

View File

@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
'''
# Import Python libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import skipIf, TestCase
from salttesting.mock import (
NO_MOCK,
NO_MOCK_REASON,
MagicMock,
patch
)
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
# Import Salt Libs
from salt.states import rabbitmq_user
rabbitmq_user.__opts__ = {}
rabbitmq_user.__salt__ = {}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class RabbitmqUserTestCase(TestCase):
'''
Test cases for salt.states.rabbitmq_user
'''
# 'present' function tests: 1
def test_present(self):
'''
Test to ensure the RabbitMQ user exists.
'''
name = 'foo'
passwd = 'password'
tag = 'user'
perms = [{'/': ['.*', '.*']}]
ret = {'name': name,
'changes': {},
'result': True,
'comment': ''}
mock = MagicMock(side_effect=[True, False, True, True,
True, True, True])
mock_dct = MagicMock(return_value={name: set(tag)})
mock_pr = MagicMock(return_value=perms)
mock_add = MagicMock(return_value={'Added': name})
with patch.dict(rabbitmq_user.__salt__,
{'rabbitmq.user_exists': mock,
'rabbitmq.list_users': mock_dct,
'rabbitmq.list_user_permissions': mock_pr,
'rabbitmq.set_user_tags': mock_add}):
comt = ('User foo already presents')
ret.update({'comment': comt})
self.assertDictEqual(rabbitmq_user.present(name), ret)
with patch.dict(rabbitmq_user.__opts__, {'test': True}):
comt = ('User foo is set to be created')
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(rabbitmq_user.present(name), ret)
comt = ("User foo's password is set to be updated")
ret.update({'comment': comt})
self.assertDictEqual(rabbitmq_user.present(name,
password=passwd,
force=True), ret)
comt = ("User foo's password is set to be removed")
ret.update({'comment': comt})
self.assertDictEqual(rabbitmq_user.present(name, force=True),
ret)
comt = ('Tags for user foo is set to be changed')
ret.update({'comment': comt})
self.assertDictEqual(rabbitmq_user.present(name, tags=tag), ret)
comt = ('Permissions for user foo is set to be changed')
ret.update({'comment': comt})
self.assertDictEqual(rabbitmq_user.present(name, perms=perms),
ret)
with patch.dict(rabbitmq_user.__opts__, {'test': False}):
ret.update({'comment': name, 'result': True,
'changes': {'new': 'Set tags: user\n', 'old': ''}})
self.assertDictEqual(rabbitmq_user.present(name, tags=tag), ret)
# 'absent' function tests: 1
def test_absent(self):
'''
Test to ensure the named user is absent.
'''
name = 'foo'
ret = {'name': name,
'changes': {},
'result': True,
'comment': 'User {0} is not present'.format(name)}
mock = MagicMock(return_value=False)
with patch.dict(rabbitmq_user.__salt__, {'rabbitmq.user_exists': mock}):
self.assertDictEqual(rabbitmq_user.absent(name), ret)
if __name__ == '__main__':
from integration import run_tests
run_tests(RabbitmqUserTestCase, needs_daemon=False)

View File

@ -0,0 +1,135 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
'''
# Import Python libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import skipIf, TestCase
from salttesting.mock import (
NO_MOCK,
NO_MOCK_REASON,
MagicMock,
patch
)
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
# Import Salt Libs
from salt.states import rbenv
rbenv.__opts__ = {}
rbenv.__salt__ = {}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class RbenvTestCase(TestCase):
'''
Test cases for salt.states.rbenv
'''
# 'installed' function tests: 1
def test_installed(self):
'''
Test to verify that the specified ruby is installed with rbenv.
'''
name = 'rbenv-deps'
ret = {'name': name,
'changes': {},
'result': True,
'comment': ''}
mock_t = MagicMock(side_effect=[False, True, True])
mock_f = MagicMock(return_value=False)
mock_def = MagicMock(return_value='2.7')
mock_ver = MagicMock(return_value=['2.7'])
with patch.dict(rbenv.__salt__,
{'rbenv.is_installed': mock_f,
'rbenv.install': mock_t,
'rbenv.default': mock_def,
'rbenv.versions': mock_ver,
'rbenv.install_ruby': mock_t}):
with patch.dict(rbenv.__opts__, {'test': True}):
comt = ('Ruby rbenv-deps is set to be installed')
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(rbenv.installed(name), ret)
with patch.dict(rbenv.__opts__, {'test': False}):
comt = ('Rbenv failed to install')
ret.update({'comment': comt, 'result': False})
self.assertDictEqual(rbenv.installed(name), ret)
comt = ('Successfully installed ruby')
ret.update({'comment': comt, 'result': True, 'default': False,
'changes': {name: 'Installed'}})
self.assertDictEqual(rbenv.installed(name), ret)
# 'absent' function tests: 1
def test_absent(self):
'''
Test to verify that the specified ruby is not installed with rbenv.
'''
name = 'myqueue'
ret = {'name': name,
'changes': {},
'result': True,
'comment': ''}
mock = MagicMock(side_effect=[False, True])
mock_def = MagicMock(return_value='2.7')
mock_ver = MagicMock(return_value=['2.7'])
with patch.dict(rbenv.__salt__,
{'rbenv.is_installed': mock,
'rbenv.default': mock_def,
'rbenv.versions': mock_ver}):
with patch.dict(rbenv.__opts__, {'test': True}):
comt = ('Ruby myqueue is set to be uninstalled')
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(rbenv.absent(name), ret)
with patch.dict(rbenv.__opts__, {'test': False}):
comt = ('Rbenv not installed, myqueue not either')
ret.update({'comment': comt, 'result': True})
self.assertDictEqual(rbenv.absent(name), ret)
comt = ('Ruby myqueue is already absent')
ret.update({'comment': comt, 'result': True})
self.assertDictEqual(rbenv.absent(name), ret)
# 'install_rbenv' function tests: 1
def test_install_rbenv(self):
'''
Test to install rbenv if not installed.
'''
name = 'myqueue'
ret = {'name': name,
'changes': {},
'result': True,
'comment': ''}
with patch.dict(rbenv.__opts__, {'test': True}):
comt = ('Rbenv is set to be installed')
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(rbenv.install_rbenv(name), ret)
with patch.dict(rbenv.__opts__, {'test': False}):
mock = MagicMock(side_effect=[False, True])
with patch.dict(rbenv.__salt__,
{'rbenv.is_installed': mock,
'rbenv.install': mock}):
comt = ('Rbenv installed')
ret.update({'comment': comt, 'result': True})
self.assertDictEqual(rbenv.install_rbenv(name), ret)
if __name__ == '__main__':
from integration import run_tests
run_tests(RbenvTestCase, needs_daemon=False)