mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 17:09:03 +00:00
Merge pull request #25008 from basepi/merge-forward-develop
Merge forward from 2015.5 to develop
This commit is contained in:
commit
576f1b8493
@ -11,6 +11,8 @@ There are a number of ways to contribute to Salt development.
|
||||
For details on how to contribute documentation improvements please review
|
||||
:ref:`Writing Salt Documentation <salt-docs>`.
|
||||
|
||||
.. _github-pull-request:
|
||||
|
||||
Sending a GitHub pull request
|
||||
=============================
|
||||
|
||||
@ -41,7 +43,7 @@ Fork a Repo Guide_>`_ and is well worth reading.
|
||||
isolated into separate branches.
|
||||
|
||||
If you're working on a fix, create your branch from the oldest release
|
||||
branch having the bug. See :ref:`Which Salt Branch?`.
|
||||
branch having the bug. See :ref:`Which Salt Branch? <which-salt-branch>`.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@ -155,13 +157,15 @@ Fork a Repo Guide_>`_ and is well worth reading.
|
||||
|
||||
Test progress and results can be found at http://jenkins.saltstack.com/.
|
||||
|
||||
.. _which-salt-branch:
|
||||
|
||||
Which Salt branch?
|
||||
==================
|
||||
|
||||
GitHub will open pull requests against Salt's main branch, ``develop``, by
|
||||
default. Ideally features should go into ``develop`` and bug fixes should go
|
||||
into the oldest supported release branch affected by the bug. See
|
||||
:ref:`Sending a GitHub pull request`.
|
||||
:ref:`Sending a GitHub pull request <github-pull-request>`.
|
||||
|
||||
If you have a bug fix and have already forked your working branch from
|
||||
``develop`` and do not know how to rebase your commits against another branch,
|
||||
@ -281,6 +285,8 @@ format-patch`_ and send them to the `salt-users`_ mailing list. The contributor
|
||||
will then get credit for the patch, and the Salt community will have an archive
|
||||
of the patch and a place for discussion.
|
||||
|
||||
.. _backporting-pull-requests:
|
||||
|
||||
Backporting Pull Requests
|
||||
=========================
|
||||
|
||||
@ -355,8 +361,8 @@ Issue and Pull Request Labeling System
|
||||
======================================
|
||||
|
||||
SaltStack uses several labeling schemes to help facilitate code contributions
|
||||
and bug resolution. See the :doc:`<labels-and-milestones>` documentation for
|
||||
more information.
|
||||
and bug resolution. See the :ref:`Labels and Milestones
|
||||
<labels-and-milestones>` documentation for more information.
|
||||
|
||||
.. _`saltstack/salt`: https://github.com/saltstack/salt
|
||||
.. _`GitHub Fork a Repo Guide`: https://help.github.com/articles/fork-a-repo
|
||||
|
@ -291,5 +291,5 @@ Issue and Pull Request Labeling System
|
||||
--------------------------------------
|
||||
|
||||
SaltStack uses several labeling schemes to help facilitate code contributions
|
||||
and bug resolution. See the :doc:`<labels-and-milestones>` documentation for
|
||||
more information.
|
||||
and bug resolution. See the :ref:`Labels and Milestones
|
||||
<labels-and-milestones>` documentation for more information.
|
||||
|
@ -4,284 +4,292 @@
|
||||
GitHub Labels and Milestones
|
||||
============================
|
||||
|
||||
SaltStack uses several labeling schemes, as well as applying milestones, to triage incoming issues and pull requests in
|
||||
the GitHub Issue Tracker. Most of the labels and milestones are used for internal tracking, but the following
|
||||
definitions might prove useful for the community to discover the best issues to help resolve.
|
||||
SaltStack uses several label categories, as well as milestones, to triage incoming issues and pull requests in the
|
||||
GitHub issue tracker. Labels are used to sort issues by type, priority, severity, status, functional area, functional
|
||||
group, and targeted release and pull requests by status, functional area, functional group, type of change, and test
|
||||
status. Milestones are used to indicate whether an issue is fully triaged or is scheduled to be fixed by SaltStack in
|
||||
an upcoming sprint.
|
||||
|
||||
Milestones
|
||||
==========
|
||||
|
||||
Milestones are most often applied to issues, as a milestone is assigned to every issue that has been triaged. However,
|
||||
milestones can also be applied to pull requests. SaltStack uses milestones to track bugs or features that should be
|
||||
included in the next major feature release, or even the next bug-fix release, as well as what issues are ready to be
|
||||
worked on or what might be blocked. All incoming issues must have a milestone associated with them.
|
||||
All issues are assigned to a milestone, whereas pull requests are almost never assigned to a milestone as the mean
|
||||
lifetime of pull requests is short enough that there is no need to track them temporally.
|
||||
|
||||
Approved
|
||||
Used to indicate that this issue has all of the needed information and is ready to be worked on.
|
||||
SaltStack uses milestones to indicate which issues are blocked on submitter or upstream actions, are approved, or are
|
||||
scheduled to be fixed or implemented in an upcoming sprint. If an issue is not attached to a sprint milestone, you are
|
||||
welcome to work on it at your own desire and convenience. If it is attached to a sprint milestone and you have already
|
||||
begun working on it or have a solution in mind or have other ideas related to the issue, you are encouraged to
|
||||
coordinate with the assignee via the GitHub issue tracker to create the best possible solution or implementation.
|
||||
|
||||
Blocked
|
||||
Used to indicate that the issue is not ready to be worked on yet. This typically applies to issues that have been
|
||||
labeled with “Info Needed”, “Question”, “Expected Behavior”, “Won’t Fix for Now”, etc.
|
||||
``Approved``
|
||||
The issue has been validated and has all necessary information.
|
||||
|
||||
Dot or Bug-fix Release
|
||||
Used to help filter/identify what issues must be fixed before the release such as 2014.7.4 or 2015.2.3. This
|
||||
milestone is often used in conjunction with the ``Blocker`` label, but not always.
|
||||
``Blocked``
|
||||
The issue is waiting on actions by parties outside of SaltStack, such as receiving more information from the
|
||||
submitter or resolution of an upstream issue. This milestone is usually applied in conjunction with the labels
|
||||
``Info Needed``, ``Question``, ``Expected Behavior``, ``Won't Fix For Now``, or ``Upstream Bug``.
|
||||
|
||||
Feature Release
|
||||
Similar to the Dot or Bug-fix Release milestone, but for upcoming feature releases such as Boron, Carbon, etc.
|
||||
This milestone is often used in conjunction with the ``Blocker`` label, but not always.
|
||||
``Under Review``
|
||||
The issue is having further validation done by a SaltStack engineer.
|
||||
|
||||
``<Sprint>``
|
||||
The issue is being actively worked on by a SaltStack engineer. Sprint milestones names are constructed from the
|
||||
chemical symbol of the next release's codename and the number of sprints until that release is made. For example,
|
||||
if the next release codename is ``Neon`` and there are five sprints until that release, the corresponding sprint
|
||||
milestone will be called ``Ne 5``. See :doc:`<topics/releases/version_numbers>` for a discussion of Salt's release
|
||||
codenames.
|
||||
|
||||
Labels
|
||||
======
|
||||
|
||||
Labels are used to facilitate the resolution of new pull requests and open issues. Most labels are confined to being
|
||||
applied to either issues or pull requests, though some labels may be applied to both.
|
||||
Labels are used to sort and describe issues and pull requests. Some labels are usually reserved for one or the other,
|
||||
though most labels may be applied to both.
|
||||
|
||||
Issue Labels
|
||||
------------
|
||||
New issues will receive at least one label and a milestone, and new pull requests will receive at least one label.
|
||||
Except for the :ref:`functional area <functional-area-labels>` and :ref:`functional group <functional-group-labels>`
|
||||
label categories, issues will generally receive only up to one label per category.
|
||||
|
||||
All incoming issues should be triaged with at least one label and a milestone. When a new issue comes in, it should be
|
||||
determined if the issue is a bug or a feature request, and either of those labels should be applied accordingly. Bugs
|
||||
and Feature Requests have differing labeling schemes, detailed below, where other labels are applied to them to further
|
||||
help contributors find issues to fix or implement.
|
||||
Type
|
||||
----
|
||||
|
||||
There are some labels, such as ``Question`` or some of the "Status" labels that may be applied as "stand alone" labels
|
||||
in which more information may be needed or a decision must be reached on how to proceed. (See the "Bug Status Labels"
|
||||
section below.)
|
||||
Issues are categorized into one of several types. Type labels are almost never used for pull requests. GitHub treats
|
||||
pull requests like issues in many ways, so a pull request could be considered an issue with an implicit ``Pull Request``
|
||||
type label applied.
|
||||
|
||||
Features
|
||||
~~~~~~~~
|
||||
``Feature``
|
||||
The issue is a request for new functionality including changes, enhancements, refactors, etc.
|
||||
|
||||
The ``Feature`` label should be applied when a user is requesting entirely new functionality. This can include new
|
||||
functions, modules, states, modular systems, flags for existing functions, etc. Features *do not* receive severity
|
||||
or priority labels, as those labels are only used for bugs. However, they may receive "Functional Area" labels or "ZD".
|
||||
``Bug``
|
||||
The issue documents broken, incorrect, or confusing behavior. This label is always accompanied by a :ref:`severity
|
||||
label <bug-severity-labels>`.
|
||||
|
||||
Feature request issues will be prioritized on an "as-needed" basis using milestones during SaltStack's feature release
|
||||
and sprint planning processes.
|
||||
``Duplicate``
|
||||
The issue is a duplicate of another feature request or bug report.
|
||||
|
||||
Bugs
|
||||
~~~~
|
||||
``Upstream Bug``
|
||||
The issue is a result of an upstream issue.
|
||||
|
||||
All bugs should have the ``Bug`` label as well as a severity, priority, functional area, and a status, as applicable.
|
||||
``Question``
|
||||
The issue is more of a question than a request for new features or a report of broken features, but can sometimes
|
||||
lead to further discussion or changes of confusing or incongruous behavior or documentation.
|
||||
|
||||
Severity
|
||||
^^^^^^^^
|
||||
|
||||
How severe is the bug? SaltStack uses four labels to determine the severity of a bug: ``Blocker``, ``Critical``,
|
||||
``High``, and ``Medium``. This scale is intended to make the bug-triage process as objective as possible.
|
||||
|
||||
Blocker
|
||||
Should be used sparingly to indicate must-have fixes for the impending release.
|
||||
|
||||
Critical
|
||||
Applied to bugs that have data loss, crashes, hanging, unresponsive system, etc.
|
||||
|
||||
High Severity
|
||||
Any bug report that contains incorrect functionality, bad functionality, a confusing user experience, etc.
|
||||
|
||||
Medium Severity
|
||||
Applied to bugs that are about cosmetic items, spelling, spacing, colors, etc.
|
||||
``Expected Behavior``
|
||||
The issue is a bug report of intended functionality.
|
||||
|
||||
Priority
|
||||
^^^^^^^^
|
||||
--------
|
||||
|
||||
In addition to using a bug severity to classify issues, a priority is also assigned to each bug to give further
|
||||
granularity in searching for bugs to fix. In this way, a bug's priority is defined as follows:
|
||||
An issue's priority is relative to its :ref:`functional area <functional-area-labels>`. If a bug report, for example,
|
||||
about ``gitfs`` indicates that all users of ``gitfs`` will encounter this bug, then a ``P1`` label will be applied, even
|
||||
though users who are not using ``gitfs`` will not encounter the bug. If a feature is requested by many users, it may be
|
||||
given a high priority.
|
||||
|
||||
P1
|
||||
Very likely. Everyone will see the bug.
|
||||
``P1``
|
||||
The issue will be seen by all users.
|
||||
|
||||
P2
|
||||
Somewhat likely. Most will see the bug, but a few will not.
|
||||
``P2``
|
||||
The issue will be seen by most users.
|
||||
|
||||
P3
|
||||
Half will see the bug, about half will not.
|
||||
``P3``
|
||||
The issue will be seen by about half of users.
|
||||
|
||||
P4
|
||||
Most will not see the bug. Usually a very specific use case or corner case.
|
||||
``P4``
|
||||
The issue will not be seen by most users. Usually the issue is a very specific use case or corner case.
|
||||
|
||||
.. note::
|
||||
.. _bug-severity-labels:
|
||||
|
||||
A bug's priority is relative to its functional area. If a bug report, for example, about ``gitfs`` includes details
|
||||
indicating that everyone who ``gitfs`` will run into this bug, then a ``P1`` label will be applied, even though
|
||||
Salt users who are not enabling ``gitfs`` will see the bug.
|
||||
Severity
|
||||
--------
|
||||
|
||||
Functional Areas
|
||||
^^^^^^^^^^^^^^^^
|
||||
Severity labels are almost always only applied to issues labeled ``Bug``.
|
||||
|
||||
All bugs should receive a "Functional Area" label to indicate what region of Salt the bug is mainly seen in. This will
|
||||
help internal developers as well as community members identify areas of expertise to find issues that can be fixed more
|
||||
easily. Functional Area labels can also be applied to Feature Requests.
|
||||
``Blocker``
|
||||
The issue is blocking an impending release.
|
||||
|
||||
Functional Area Labels, in alphabetical order, include:
|
||||
``Critical``
|
||||
The issue causes data loss, crashes or hangs salt processes, makes the system unresponsive, etc.
|
||||
|
||||
* Core
|
||||
* Documentation
|
||||
* Execution Module
|
||||
* File Servers
|
||||
* Multi-Master
|
||||
* Packaging
|
||||
* Pillar
|
||||
* Platform Mgmt.
|
||||
* RAET
|
||||
* Returners
|
||||
* Salt-API
|
||||
* Salt-Cloud
|
||||
* Salt-SSH
|
||||
* Salt-Syndic
|
||||
* State Module
|
||||
* Windows
|
||||
* ZMQ
|
||||
``High Severity``
|
||||
The issue reports incorrect functionality, bad functionality, a confusing user experience, etc.
|
||||
|
||||
Bug Status Labels
|
||||
^^^^^^^^^^^^^^^^^
|
||||
``Medium Severity``
|
||||
The issue reports cosmetic items, formatting, spelling, colors, etc.
|
||||
|
||||
Status lables are used to define and track the state a bug is in at any given time. Not all bugs will have a status
|
||||
label, but if a SaltStack employee is able to apply a status label, he or she will. Status labels are somewhat unique
|
||||
in the fact that they might be the only label on an issue, such as ``Pending Discussion``, ``Info Needed``, or
|
||||
``Expected Behavior`` until further action can be taken.
|
||||
.. _functional-area-labels:
|
||||
|
||||
Cannot Reproduce
|
||||
Someone from the SaltStack team has tried to reproduce the bug with the given information but they are unable to
|
||||
replicate the problem. More information will need to be provided from the original issue-filer before proceeding.
|
||||
Functional Area
|
||||
---------------
|
||||
|
||||
Confirmed
|
||||
A SaltStack engineer has confirmed the reported bug and provided a simple way to reproduce the failure.
|
||||
Many major components of Salt have corresponding GitHub labels. These labels are applied to all issues and pull
|
||||
requests as is reasonably appropriate. They are useful in organizing issues and pull requests according to the source
|
||||
code relevant to issues or the source code changed by pull requests.
|
||||
|
||||
Duplicate
|
||||
The issue has been reported already in another report. A link to the other bug report must be provided. At that
|
||||
point the new issue can be closed. Usually, the earliest bug on file is kept as that typically has the most
|
||||
discussion revolving around the issue, though not always. (This can be a "stand-alone" label.)
|
||||
* ``Execution Module``
|
||||
* ``File Servers``
|
||||
* ``Grains``
|
||||
* ``Multi-Master``
|
||||
* ``Packaging`` Related to packaging of Salt, not Salt's support for package management.
|
||||
* ``Pillar``
|
||||
* ``RAET``
|
||||
* ``Returners``
|
||||
* ``Runners``
|
||||
* ``Salt-API``
|
||||
* ``Salt-Cloud``
|
||||
* ``Salt-SSH``
|
||||
* ``Salt-Syndic``
|
||||
* ``State Module``
|
||||
* ``Tests``
|
||||
* ``Transport``
|
||||
* ``Windows``
|
||||
* ``ZMQ``
|
||||
|
||||
Expected Behavior
|
||||
The issue reported is expected behavior and nothing needs to be fixed. (This can be a "stand-alone" label.)
|
||||
.. _functional-group-labels:
|
||||
|
||||
Fixed Pending Verification
|
||||
The bug has been fixed and a link to the applicable pull request(s) has been provided, but confirmation is being
|
||||
sought from the community member(s) involved in the bug to test and confirm the fix.
|
||||
Functional Group
|
||||
----------------
|
||||
|
||||
Info Needed
|
||||
More information about the issue is needed before proceeding such as a versions report, a sample state, the command
|
||||
the user was running, or the operating system the error was occurring on, etc. (This can be a "stand-alone" label.)
|
||||
These labels sort issues and pull requests according to the internal SaltStack engineering teams.
|
||||
|
||||
Upstream Bug
|
||||
The reported bug is something that cannot be fixed in the Salt code base but is instead a bug in another library
|
||||
such a bug in ZMQ or Python. When an issue is labeled with ``Upstream Bug`` then a bug report in the upstream
|
||||
project must be filed (or found if a report already exists) and a link to the report must be provided to the issue
|
||||
in Salt for tracking purposes. (This can be a stand-alone label.)
|
||||
``Core``
|
||||
The issue or pull request relates to code that is central or existential to Salt itself.
|
||||
|
||||
Won't Fix for Now
|
||||
The SaltStack team has acknowledged the issue at hand is legitimate, but made the call that it’s not something
|
||||
they’re able or willing to fix at this time. These issues may be revisited in the future.
|
||||
``Platform``
|
||||
The issue or pull request relates to support and integration with various platforms like traditional operating
|
||||
systems as well as containers, platform-based utilities like filesystems, command schedulers, etc., and
|
||||
system-based applications like webservers, databases, etc.
|
||||
|
||||
Other
|
||||
~~~~~
|
||||
``RIoT``
|
||||
The issue or pull request relates to support and integration with various abstract systems like cloud providers,
|
||||
hypervisors, API-based services, etc.
|
||||
|
||||
There are a couple of other labels that are helpful in categorizing bugs that are not included in the categories above.
|
||||
These labels can either stand on their own such as ``Question`` or can be applied to bugs or feature requests as
|
||||
applicable.
|
||||
``Console``
|
||||
The issue or pull request relates to the SaltStack enterprise console.
|
||||
|
||||
Low Hanging Fruit
|
||||
Applied to bugs that should be easy to fix. This is useful for new contributors to know where some simple things
|
||||
are to get involved in contributing to salt.
|
||||
``Documentation``
|
||||
The issue or pull request relates to documentation.
|
||||
|
||||
Question
|
||||
Used when the issue isn’t a bug nor a feature, but the user has a question about expected behavior, how something
|
||||
works, is misunderstanding a concept, etc. This label is typically applied on its own with ``Blocked`` milestone.
|
||||
Status
|
||||
------
|
||||
|
||||
Regression
|
||||
Helps with additional filtering for bug fixing. If something previously worked and now does not work, as opposed to
|
||||
something that never worked in the first place, the issue should be treated with greater urgency.
|
||||
Status labels are used to define and track the state of issues and pull requests. Not all potential statuses correspond
|
||||
to a label, but some statuses are common enough that labels have been created for them. If an issue has not been moved
|
||||
beyond the ``Blocked`` milestone, it is very likely that it will only have a status label.
|
||||
|
||||
ZD
|
||||
Stands for “Zendesk” and is used to help track bugs that customers are seeing as well as community members. Bugs
|
||||
with this label should be treated with greater urgency.
|
||||
``Bugfix - back-port``
|
||||
The pull request needs to be back-ported to an older release branch. This is done by :ref:`recreating the pull
|
||||
request <backporting-pull-requests>` against that branch. Once the back-port is completed, this label is replaced
|
||||
with a ``Bugfix - [Done] back-ported`` label. Normally, new features should go into the develop and bug fixes into
|
||||
the oldest supported release branch, see :ref:`<which-salt-branch>`.
|
||||
|
||||
Pull Request Labels
|
||||
-------------------
|
||||
``Bugfix - [Done] back-ported``
|
||||
The pull request has been back-ported to an older branch.
|
||||
|
||||
SaltStack also applies various labels to incoming pull requests. These are mainly used to help SaltStack engineers
|
||||
easily identify the nature the changes presented in a pull request and whether or not that pull request is ready to be
|
||||
reviewed and merged into the Salt codebase.
|
||||
``Cannot Reproduce``
|
||||
The issue is a bug and has been reviewed by a SaltStack engineer, but it cannot be replicated with the provided
|
||||
information and context. Those involved with the bug will need to work through additional ideas until the bug can
|
||||
be isolated and verified.
|
||||
|
||||
``Confirmed``
|
||||
The issue is a bug and has been confirmed by a SaltStack engineer, who often documents a minimal working example
|
||||
that reproduces the bug.
|
||||
|
||||
``Fixed Pending Verification``
|
||||
The issue is a bug and has been fixed by one or more pull requests, which should link to the issue. Closure of the
|
||||
issue is contingent upon confirmation of resolution from the submitter. If the submitter reports a negative
|
||||
confirmation, this label is removed. If no response is given after a few weeks, then the issue will be assumed
|
||||
fixed and closed.
|
||||
|
||||
``Info Needed``
|
||||
The issue needs more information before it can be verified and resolved. For a feature request this may include a
|
||||
description of the use cases. Almost all bug reports need to include at least the versions of salt and its
|
||||
dependencies, the system type and version, commands used, debug logs, error messages, and relevant configs.
|
||||
|
||||
``Pending Changes``
|
||||
The pull request needs additional changes before it can be merged.
|
||||
|
||||
``Pending Discussion``
|
||||
The issue or pull request needs more discussion before it can be closed or merged. The status of the issue or pull
|
||||
request is not clear or apparent enough for definite action to be taken, or additional input from SaltStack, the
|
||||
submitter, or another party has been requested.
|
||||
|
||||
If the issue is not a pull request, once the discussion has arrived at a cogent conclusion, this label will be
|
||||
removed and the issue will be accepted. If it is a pull request, the results of the discussion may require
|
||||
additional changes and thus, a ``Pending Changes`` label.
|
||||
|
||||
``Won't Fix for Now``
|
||||
The issue is legitimate, but it is not something the SaltStack team is currently able or willing to fix or
|
||||
implement. Issues having this label may be revisited in the future.
|
||||
|
||||
Type of Change
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
A "* Change" label is applied to each incoming pull request. The type of change label that is applied to a pull request
|
||||
is based on a scale that encompasses the number of lines affected by the change in conjunction with the area of code
|
||||
the change touches (i.e. core code areas vs. execution or state modules).
|
||||
Every pull request should receive a change label. These labels measure the quantity of change as well as the
|
||||
significance of the change. The amount of change and the importance of the code area changed are considered, but often
|
||||
the depth of secondary code review required and the potential repercussions of the change may also advise the label
|
||||
choice.
|
||||
|
||||
The conditions given for these labels are recommendations, as the pull request reviewer will also consult their
|
||||
intuition and experience regarding the magnitude of the impact of the proposed changes in the pull request.
|
||||
Core code areas include: state compiler, crypto engine, master and minion and syndic daemons, transport, pillar
|
||||
rendering, loader, transport layer, event system, salt.utils, client, cli, logging, netapi, runner engine, templating
|
||||
engine, top file compilation, file client, file server, mine, salt-ssh, test runner, etc.
|
||||
|
||||
Core code areas include: state compiler, crypto engine, master and minion, transport, pillar rendering, loader,
|
||||
transport layer, event system, salt.utils, client, cli, logging, netapi, runner engine, templating engine, top file
|
||||
compilation, file client, file server, mine, salt-ssh, test runner, etc.
|
||||
Non-core code usually constitutes the specific set of plugins for each of the several plugin layers of Salt: execution
|
||||
modules, states, runners, returners, clouds, etc.
|
||||
|
||||
* Minor Change
|
||||
``Minor Change``
|
||||
* Less than 64 lines changed, or
|
||||
* Less than 8 core lines changed
|
||||
* Medium Change
|
||||
``Medium Change``
|
||||
* Less than 256 lines changed, or
|
||||
* Less than 64 core lines changed
|
||||
* Master Change
|
||||
``Master Change``
|
||||
* More than 256 lines changed, or
|
||||
* More than 64 core lines changed
|
||||
* Expert Change
|
||||
``Expert Change``
|
||||
* Needs specialized, in-depth review
|
||||
|
||||
Back-port Labels
|
||||
~~~~~~~~~~~~~~~~
|
||||
Test Status
|
||||
-----------
|
||||
|
||||
There are two labels that are used to keep track of what pull requests need to be back-ported to an older release branch
|
||||
and which pull requests have already been back-ported.
|
||||
These labels relate to the status of the automated tests that run on pull requests. If the tests on a pull request fail
|
||||
and are not overridden by one of these labels, the pull request submitter needs to update the code and/or tests so that
|
||||
the tests pass and the pull request can be merged.
|
||||
|
||||
Bugfix - back-port
|
||||
Indicates a pull request that needs to be back-ported. Once the back-port is completed, the back-porting pull request
|
||||
is linked to the original pull request and this label is removed.
|
||||
``Lint``
|
||||
The pull request has passed all tests except for the code lint checker.
|
||||
|
||||
Bugfix - [Done] back-ported
|
||||
Indicates a pull request that has been back-ported to another branch. The pull request that is responsible for the
|
||||
backport should be linked to this original pull request.
|
||||
``Tests Passed``
|
||||
The pull request has passed all tests even though some test results are negative. Sometimes the automated testing
|
||||
infrastructure will encounter internal errors unrelated to the code change in the pull request that cause test runs
|
||||
to fail. These errors can be caused by cloud provider and network issues and also Jenkins issues like erroneously
|
||||
accumulating workspace artifacts, resource exhaustion, and bugs that arise from long running Jenkins processes.
|
||||
|
||||
Testing Labels
|
||||
~~~~~~~~~~~~~~
|
||||
Other
|
||||
-----
|
||||
|
||||
There are a couple of labels that the QA team uses to indicate the mergability of a pull request. If the pull request is
|
||||
legitimately passing or failing tests, then one or more of these labels may be applied.
|
||||
These labels indicate miscellaneous issue types or statuses that are common or important enough to be tracked and sorted
|
||||
with labels.
|
||||
|
||||
Lint
|
||||
If a pull request fails the test run, but the only failures are related pylint errors, this label will be applied to
|
||||
indicate that pylint needs to be fixed before proceeding.
|
||||
``Awesome``
|
||||
The pull request implements an especially well crafted solution, or a very difficult but necessary change.
|
||||
|
||||
Pending Changes
|
||||
Indicates that additional commits should be added to the original pull request before the pull request is merged
|
||||
into the codebase. These changes are unrelated to fixing tests and are generally needed to round out any unfinished
|
||||
pull requests.
|
||||
``Low Hanging Fruit``
|
||||
The issue is trivial or almost trivial to implement or fix. Issues having this label should be a good starting
|
||||
place for new contributors to Salt.
|
||||
|
||||
Tests Passed
|
||||
Sometimes the Jenkins test run encounters problems, either tests that are known to have reliability issues or a
|
||||
test VM failed to build, but the problems are not related to the code changed in the pull request. This label is
|
||||
used to indicate that someone has reviewed the test failures and has deemed the failures to be non-pertinent.
|
||||
``Needs Testcase``
|
||||
The issue or pull request relates to a feature that needs test coverage. The pull request containing the tests
|
||||
should reference the issue or pull request having this label, whereupon the label should be removed.
|
||||
|
||||
Other Pull Request Labels
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
``Regression``
|
||||
The issue is a bug that breaks functionality known to work in previous releases.
|
||||
|
||||
Awesome
|
||||
Applied to pull requests that implemented a cool new feature or fixed a bug in an excellent way.
|
||||
``Story``
|
||||
The issue is used by a SaltStack engineer to track progress on multiple related issues in a single place.
|
||||
|
||||
Labels that Bridge Issues and Pull Requests
|
||||
===========================================
|
||||
``ZD``
|
||||
The issue is related to a Zendesk customer support ticket.
|
||||
|
||||
Needs Testcase
|
||||
Used by SaltStack's QA team to realize where pain points are and to bring special attention to where some test
|
||||
coverage needs to occur, especially in areas that have regressed. This label can apply to issues or pull requests,
|
||||
which can also be open or closed. Once tests are written, the pull request containing the tests should be linked to
|
||||
the issue or pull request that originally had the ``Needs Testcase`` label. At this point, the ``Needs Testcase``
|
||||
label must be removed to indicate that tests no longer need to be written.
|
||||
|
||||
Pending Discussion
|
||||
If this label is applied to an issue, the issue may or may not be a bug. Enough information was provided about the
|
||||
issue, but some other opinions on the issue are desirable before proceeding. (This can be a "stand-alone" label.)
|
||||
If the label is applied to a pull request, this is used to signal that further discussion must occur before a
|
||||
decision is made to either merge the pull request into the code base or to close it all together.
|
||||
``<Release>``
|
||||
The issue is scheduled to be implemented by ``<Release>``. See :doc:`<topics/releases/version_numbers>` for a
|
||||
discussion of Salt's release codenames.
|
||||
|
@ -14,7 +14,7 @@
|
||||
grouping="require_all"
|
||||
restart_on="none"
|
||||
type="path">
|
||||
<service_fmri value='file:///opt/local/etc/salt/minion'/>
|
||||
<service_fmri value="file:///opt/local/etc/salt/minion"/>
|
||||
</dependency>
|
||||
|
||||
<dependency name="network"
|
||||
@ -31,17 +31,17 @@
|
||||
<service_fmri value="svc:/system/filesystem/local"/>
|
||||
</dependency>
|
||||
|
||||
<method_context>
|
||||
<method_environment>
|
||||
<envvar name='PATH'
|
||||
value='/usr/local/sbin:/usr/local/bin:/opt/local/sbin:/opt/local/bin:/usr/sbin:/usr/bin:/sbin'/>
|
||||
</method_environment>
|
||||
</method_context>
|
||||
|
||||
<exec_method type="method"
|
||||
name="start"
|
||||
exec="/opt/local/bin/salt-minion -c %{config_dir}"
|
||||
timeout_seconds="60"/>
|
||||
timeout_seconds="60">
|
||||
<method_context>
|
||||
<method_environment>
|
||||
<envvar name="PATH"
|
||||
value="/usr/local/sbin:/usr/local/bin:/opt/local/sbin:/opt/local/bin:/usr/sbin:/usr/bin:/sbin"/>
|
||||
</method_environment>
|
||||
</method_context>
|
||||
</exec_method>
|
||||
|
||||
<exec_method type="method"
|
||||
name="stop"
|
||||
|
@ -1365,6 +1365,9 @@ class Cloud(object):
|
||||
except IOError:
|
||||
main_cloud_config = {}
|
||||
|
||||
if main_cloud_config is None:
|
||||
main_cloud_config = {}
|
||||
|
||||
profile_details = self.opts['profiles'][profile]
|
||||
alias, driver = profile_details['provider'].split(':')
|
||||
mapped_providers = self.map_providers_parallel()
|
||||
@ -1540,9 +1543,13 @@ class Cloud(object):
|
||||
# Mis-configured provider that got removed?
|
||||
log.warn(
|
||||
'The cloud driver, {0!r}, configured under the '
|
||||
'{1!r} cloud provider alias was not loaded since '
|
||||
'\'{2}()\' could not be found. Removing it from '
|
||||
'the available providers list.'.format(
|
||||
'{1!r} cloud provider alias, could not be loaded. '
|
||||
'Please check your provider configuration files and '
|
||||
'ensure all required dependencies are installed '
|
||||
'for the {0!r} driver.\n'
|
||||
'In rare cases, this could indicate the \'{2}()\' '
|
||||
'function could not be found.\nRemoving {0!r} from '
|
||||
'the available providers list'.format(
|
||||
driver, alias, fun
|
||||
)
|
||||
)
|
||||
|
@ -80,19 +80,22 @@ def get_configured_provider():
|
||||
'''
|
||||
Return the first configured instance.
|
||||
'''
|
||||
warn_until(
|
||||
'Beryllium',
|
||||
'The digital_ocean driver is deprecated and will be removed in Salt Beryllium. '
|
||||
'Please convert your digital ocean provider configs to use the digital_ocean_v2 '
|
||||
'driver.'
|
||||
)
|
||||
|
||||
return config.is_provider_configured(
|
||||
configuration = config.is_provider_configured(
|
||||
__opts__,
|
||||
__active_provider_name__ or 'digital_ocean',
|
||||
('personal_access_token',)
|
||||
)
|
||||
|
||||
if configuration:
|
||||
warn_until(
|
||||
'Beryllium',
|
||||
'The digital_ocean driver is deprecated and will be removed in Salt Beryllium. '
|
||||
'Please convert your digital ocean provider configs to use the digital_ocean_v2 '
|
||||
'driver.'
|
||||
)
|
||||
|
||||
return configuration
|
||||
|
||||
|
||||
def avail_locations(call=None):
|
||||
'''
|
||||
|
@ -1348,14 +1348,13 @@ def prepend_root_dir(opts, path_options):
|
||||
'root_dir' option.
|
||||
'''
|
||||
root_dir = os.path.abspath(opts['root_dir'])
|
||||
root_opt = opts['root_dir'].rstrip(os.sep)
|
||||
for path_option in path_options:
|
||||
if path_option in opts:
|
||||
if opts[path_option].startswith(opts['root_dir']):
|
||||
opts[path_option] = opts[path_option][len(opts['root_dir']):]
|
||||
opts[path_option] = salt.utils.path_join(
|
||||
root_dir,
|
||||
opts[path_option]
|
||||
)
|
||||
path = opts[path_option]
|
||||
if path == root_opt or path.startswith(root_opt + os.sep):
|
||||
path = path[len(root_opt):]
|
||||
opts[path_option] = salt.utils.path_join(root_dir, path)
|
||||
|
||||
|
||||
def insert_system_path(opts, paths):
|
||||
|
@ -365,12 +365,15 @@ class Client(object):
|
||||
localsfilesdest = os.path.join(
|
||||
self.opts['cachedir'], 'localfiles', path.lstrip('|/'))
|
||||
filesdest = os.path.join(
|
||||
self.opts['cachedir'], 'files', saltenv, path.lstrip('|'))
|
||||
self.opts['cachedir'], 'files', saltenv, path.lstrip('|/'))
|
||||
extrndest = self._extrn_path(path, saltenv)
|
||||
|
||||
if os.path.exists(filesdest):
|
||||
return salt.utils.url.escape(filesdest) if escaped else filesdest
|
||||
elif os.path.exists(localsfilesdest):
|
||||
return salt.utils.url.escape(localsfilesdest) if escaped else localsfilesdest
|
||||
elif os.path.exists(extrndest):
|
||||
return extrndest
|
||||
|
||||
return ''
|
||||
|
||||
@ -546,13 +549,7 @@ class Client(object):
|
||||
netloc = salt.utils.sanitize_win_path_string(url_data.netloc)
|
||||
else:
|
||||
netloc = url_data.netloc
|
||||
dest = salt.utils.path_join(
|
||||
self.opts['cachedir'],
|
||||
'extrn_files',
|
||||
saltenv,
|
||||
netloc,
|
||||
url_data.path
|
||||
)
|
||||
dest = self._extrn_path(url, saltenv)
|
||||
destdir = os.path.dirname(dest)
|
||||
if not os.path.isdir(destdir):
|
||||
os.makedirs(destdir)
|
||||
@ -681,13 +678,7 @@ class Client(object):
|
||||
return ''
|
||||
if not dest:
|
||||
# No destination passed, set the dest as an extrn_files cache
|
||||
dest = salt.utils.path_join(
|
||||
self.opts['cachedir'],
|
||||
'extrn_files',
|
||||
saltenv,
|
||||
url_data.netloc,
|
||||
url_data.path
|
||||
)
|
||||
dest = self._extrn_path(url, saltenv)
|
||||
# If Salt generated the dest name, create any required dirs
|
||||
makedirs = True
|
||||
|
||||
@ -701,6 +692,20 @@ class Client(object):
|
||||
shutil.move(data['data'], dest)
|
||||
return dest
|
||||
|
||||
def _extrn_path(self, url, saltenv):
|
||||
'''
|
||||
Return the extn_filepath for a given url
|
||||
'''
|
||||
url_data = urlparse(url)
|
||||
|
||||
return salt.utils.path_join(
|
||||
self.opts['cachedir'],
|
||||
'extrn_files',
|
||||
saltenv,
|
||||
url_data.netloc,
|
||||
url_data.path
|
||||
)
|
||||
|
||||
|
||||
class LocalClient(Client):
|
||||
'''
|
||||
|
@ -3144,8 +3144,61 @@ def get_managed(
|
||||
# Copy the file to the minion and templatize it
|
||||
sfn = ''
|
||||
source_sum = {}
|
||||
# if we have a source defined, lets figure out what the hash is
|
||||
if source:
|
||||
urlparsed_source = _urlparse(source)
|
||||
if urlparsed_source.scheme == 'salt':
|
||||
source_sum = __salt__['cp.hash_file'](source, saltenv)
|
||||
if not source_sum:
|
||||
return '', {}, 'Source file {0} not found'.format(source)
|
||||
# if its a local file
|
||||
elif urlparsed_source.scheme == 'file':
|
||||
source_sum = get_hash(urlparsed_source.path)
|
||||
elif source.startswith('/'):
|
||||
source_sum = get_hash(source)
|
||||
elif source_hash:
|
||||
protos = ('salt', 'http', 'https', 'ftp', 'swift')
|
||||
if _urlparse(source_hash).scheme in protos:
|
||||
# The source_hash is a file on a server
|
||||
hash_fn = __salt__['cp.cache_file'](source_hash, saltenv)
|
||||
if not hash_fn:
|
||||
return '', {}, 'Source hash file {0} not found'.format(
|
||||
source_hash)
|
||||
source_sum = extract_hash(hash_fn, '', name)
|
||||
if source_sum is None:
|
||||
return '', {}, ('Source hash file {0} contains an invalid '
|
||||
'hash format, it must be in the format <hash type>=<hash>.'
|
||||
).format(source_hash)
|
||||
|
||||
else:
|
||||
# The source_hash is a hash string
|
||||
comps = source_hash.split('=')
|
||||
if len(comps) < 2:
|
||||
return '', {}, ('Source hash file {0} contains an '
|
||||
'invalid hash format, it must be in '
|
||||
'the format <hash type>=<hash>'
|
||||
).format(source_hash)
|
||||
source_sum['hsum'] = comps[1].strip()
|
||||
source_sum['hash_type'] = comps[0].strip()
|
||||
else:
|
||||
return '', {}, ('Unable to determine upstream hash of'
|
||||
' source file {0}').format(source)
|
||||
|
||||
# if the file is a template we need to actually template the file to get
|
||||
# a checksum, but we can cache the template itself, but only if there is
|
||||
# a template source (it could be a templated contents)
|
||||
if template and source:
|
||||
sfn = __salt__['cp.cache_file'](source, saltenv)
|
||||
# check if we have the template cached
|
||||
template_dest = __salt__['cp.is_cached'](source, saltenv)
|
||||
if template_dest:
|
||||
comps = source_hash.split('=')
|
||||
cached_template_sum = get_hash(template_dest, form=source_sum['hash_type'])
|
||||
if cached_template_sum == source_sum['hsum']:
|
||||
sfn = template_dest
|
||||
# if we didn't have the template file, lets get it
|
||||
if not sfn:
|
||||
sfn = __salt__['cp.cache_file'](source, saltenv)
|
||||
|
||||
# exists doesn't play nice with sfn as bool
|
||||
# but if cache failed, sfn == False
|
||||
if not sfn or not os.path.exists(sfn):
|
||||
@ -3184,40 +3237,7 @@ def get_managed(
|
||||
else:
|
||||
__clean_tmp(sfn)
|
||||
return sfn, {}, data['data']
|
||||
else:
|
||||
# Copy the file down if there is a source
|
||||
if source:
|
||||
if _urlparse(source).scheme == 'salt':
|
||||
source_sum = __salt__['cp.hash_file'](source, saltenv)
|
||||
if not source_sum:
|
||||
return '', {}, 'Source file {0!r} not found'.format(source)
|
||||
elif source_hash:
|
||||
protos = ['salt', 'http', 'https', 'ftp', 'swift', 's3', 'file']
|
||||
if _urlparse(source_hash).scheme in protos:
|
||||
# The source_hash is a file on a server
|
||||
hash_fn = __salt__['cp.cache_file'](source_hash, saltenv)
|
||||
if not hash_fn:
|
||||
return '', {}, 'Source hash file {0} not found'.format(
|
||||
source_hash)
|
||||
source_sum = extract_hash(hash_fn, '', name)
|
||||
if source_sum is None:
|
||||
return '', {}, ('Source hash file {0} contains an invalid '
|
||||
'hash format, it must be in the format <hash type>=<hash>.'
|
||||
).format(source_hash)
|
||||
|
||||
else:
|
||||
# The source_hash is a hash string
|
||||
comps = source_hash.split('=')
|
||||
if len(comps) < 2:
|
||||
return '', {}, ('Source hash file {0} contains an '
|
||||
'invalid hash format, it must be in '
|
||||
'the format <hash type>=<hash>'
|
||||
).format(source_hash)
|
||||
source_sum['hsum'] = comps[1].strip()
|
||||
source_sum['hash_type'] = comps[0].strip()
|
||||
else:
|
||||
return '', {}, ('Unable to determine upstream hash of'
|
||||
' source file {0}').format(source)
|
||||
return sfn, source_sum, ''
|
||||
|
||||
|
||||
|
@ -218,7 +218,6 @@ def list_keys(user=None, gnupghome=None):
|
||||
salt '*' gpg.list_keys
|
||||
|
||||
'''
|
||||
log.debug('GPG_1_3_1 {0}'.format(GPG_1_3_1))
|
||||
_keys = []
|
||||
for _key in _list_keys(user, gnupghome):
|
||||
tmp = {}
|
||||
@ -641,22 +640,36 @@ def import_key(user=None,
|
||||
except IOError:
|
||||
raise SaltInvocationError('filename does not exist.')
|
||||
|
||||
import_result = gpg.import_keys(text)
|
||||
counts = import_result.counts
|
||||
log.debug('imported_data {0}'.format(list(import_result.__dict__.keys())))
|
||||
log.debug('imported_data {0}'.format(counts))
|
||||
imported_data = gpg.import_keys(text)
|
||||
|
||||
if counts.get('imported') or counts.get('imported_rsa'):
|
||||
ret['message'] = 'Successfully imported key(s).'
|
||||
elif counts.get('unchanged'):
|
||||
ret['message'] = 'Key(s) already exist in keychain.'
|
||||
elif counts.get('not_imported'):
|
||||
ret['res'] = False
|
||||
ret['message'] = 'Unable to import key.'
|
||||
elif not counts.get('count'):
|
||||
ret['res'] = False
|
||||
ret['message'] = 'Unable to import key.'
|
||||
# include another check for Salt unit tests
|
||||
gnupg_version = distutils.version.LooseVersion(gnupg.__version__)
|
||||
if gnupg_version >= '1.3.1':
|
||||
GPG_1_3_1 = True
|
||||
|
||||
if GPG_1_3_1:
|
||||
counts = imported_data.counts
|
||||
if counts.get('imported') or counts.get('imported_rsa'):
|
||||
ret['message'] = 'Successfully imported key(s).'
|
||||
elif counts.get('unchanged'):
|
||||
ret['message'] = 'Key(s) already exist in keychain.'
|
||||
elif counts.get('not_imported'):
|
||||
ret['res'] = False
|
||||
ret['message'] = 'Unable to import key.'
|
||||
elif not counts.get('count'):
|
||||
ret['res'] = False
|
||||
ret['message'] = 'Unable to import key.'
|
||||
else:
|
||||
if imported_data.imported or imported_data.imported_rsa:
|
||||
ret['message'] = 'Successfully imported key(s).'
|
||||
elif imported_data.unchanged:
|
||||
ret['message'] = 'Key(s) already exist in keychain.'
|
||||
elif imported_data.not_imported:
|
||||
ret['res'] = False
|
||||
ret['message'] = 'Unable to import key.'
|
||||
elif not imported_data.count:
|
||||
ret['res'] = False
|
||||
ret['message'] = 'Unable to import key.'
|
||||
return ret
|
||||
|
||||
|
||||
|
@ -1164,3 +1164,30 @@ def default_route(family=None):
|
||||
ret.append(route)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def get_route(ip):
|
||||
'''
|
||||
Return routing information for given destination ip
|
||||
|
||||
.. versionadded:: 2015.5.3
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' network.get_route 10.10.10.10
|
||||
'''
|
||||
|
||||
if __grains__['kernel'] == 'Linux':
|
||||
cmd = 'ip route get {0}'.format(ip)
|
||||
out = __salt__['cmd.run'](cmd, python_shell=True)
|
||||
regexp = re.compile(r'(via\s+(?P<gateway>[\w\.:]+))?\s+dev\s+(?P<interface>[\w\.\:]+)\s+.*src\s+(?P<source>[\w\.:]+)')
|
||||
m = regexp.search(out.splitlines()[0])
|
||||
ret = {
|
||||
'destination': ip,
|
||||
'gateway': m.group('gateway'),
|
||||
'interface': m.group('interface'),
|
||||
'source': m.group('source')}
|
||||
|
||||
return ret
|
||||
else:
|
||||
raise CommandExecutionError('Not yet supported on this platform')
|
||||
|
@ -119,10 +119,13 @@ def install(pkg=None,
|
||||
elif pkgs:
|
||||
cmd += ' "{0}"'.format('" "'.join(pkgs))
|
||||
|
||||
if env is None:
|
||||
env = {}
|
||||
|
||||
if runas:
|
||||
uid = salt.utils.get_uid(runas)
|
||||
if uid:
|
||||
env.update({'SUDO_UID': uid, 'SUDO_USER': ''})
|
||||
env.update({'SUDO_UID': b'{0}'.format(uid), 'SUDO_USER': b''})
|
||||
|
||||
result = __salt__['cmd.run_all'](cmd, python_shell=False, cwd=dir, runas=runas, env=env)
|
||||
|
||||
@ -188,10 +191,13 @@ def uninstall(pkg,
|
||||
|
||||
'''
|
||||
|
||||
if env is None:
|
||||
env = {}
|
||||
|
||||
if runas:
|
||||
uid = salt.utils.get_uid(runas)
|
||||
if uid:
|
||||
env.update({'SUDO_UID': uid, 'SUDO_USER': ''})
|
||||
env.update({'SUDO_UID': b'{0}'.format(uid), 'SUDO_USER': b''})
|
||||
|
||||
cmd = 'npm uninstall'
|
||||
|
||||
@ -245,10 +251,13 @@ def list_(pkg=None,
|
||||
|
||||
'''
|
||||
|
||||
if env is None:
|
||||
env = {}
|
||||
|
||||
if runas:
|
||||
uid = salt.utils.get_uid(runas)
|
||||
if uid:
|
||||
env.update({'SUDO_UID': uid, 'SUDO_USER': ''})
|
||||
env.update({'SUDO_UID': b'{0}'.format(uid), 'SUDO_USER': b''})
|
||||
|
||||
cmd = 'npm list --silent --json'
|
||||
|
||||
|
@ -268,7 +268,8 @@ class EventListener(object):
|
||||
# request_obj -> list of (tag, future)
|
||||
self.request_map = defaultdict(list)
|
||||
|
||||
self.timeout_map = {} # map of future -> timeout_callback
|
||||
# map of future -> timeout_callback
|
||||
self.timeout_map = {}
|
||||
|
||||
self.stream = zmqstream.ZMQStream(self.event.sub,
|
||||
io_loop=tornado.ioloop.IOLoop.current())
|
||||
@ -281,7 +282,14 @@ class EventListener(object):
|
||||
if request not in self.request_map:
|
||||
return
|
||||
for tag, future in self.request_map[request]:
|
||||
# timeout the future
|
||||
self._timeout_future(tag, future)
|
||||
# remove the timeout
|
||||
if future in self.timeout_map:
|
||||
tornado.ioloop.IOLoop.current().remove_timeout(self.timeout_map[future])
|
||||
del self.timeout_map[future]
|
||||
|
||||
del self.request_map[request]
|
||||
|
||||
def get_event(self,
|
||||
request,
|
||||
@ -292,6 +300,13 @@ class EventListener(object):
|
||||
'''
|
||||
Get an event (async of course) return a future that will get it later
|
||||
'''
|
||||
# if the request finished, no reason to allow event fetching, since we
|
||||
# can't send back to the client
|
||||
if request._finished:
|
||||
future = Future()
|
||||
future.set_exception(TimeoutException())
|
||||
return future
|
||||
|
||||
future = Future()
|
||||
if callback is not None:
|
||||
def handle_future(future):
|
||||
|
@ -1138,14 +1138,15 @@ def installed(
|
||||
|
||||
if modified_hold:
|
||||
for i in modified_hold:
|
||||
comment.append(i['comment'])
|
||||
change_name = i['name']
|
||||
if len(changes[change_name]['new']) > 0:
|
||||
changes[change_name]['new'] += '\n'
|
||||
changes[change_name]['new'] += '{0}'.format(i['changes']['new'])
|
||||
if len(changes[change_name]['old']) > 0:
|
||||
changes[change_name]['old'] += '\n'
|
||||
changes[change_name]['old'] += '{0}'.format(i['changes']['old'])
|
||||
if change_name in changes:
|
||||
comment.append(i['comment'])
|
||||
if len(changes[change_name]['new']) > 0:
|
||||
changes[change_name]['new'] += '\n'
|
||||
changes[change_name]['new'] += '{0}'.format(i['changes']['new'])
|
||||
if len(changes[change_name]['old']) > 0:
|
||||
changes[change_name]['old'] += '\n'
|
||||
changes[change_name]['old'] += '{0}'.format(i['changes']['old'])
|
||||
|
||||
# Any requested packages that were not targeted for install or reinstall
|
||||
if not_modified:
|
||||
|
@ -103,7 +103,7 @@ def top(**kwargs):
|
||||
environment_field = __opts__['master_tops']['mongo'].get('environment_field', 'environment')
|
||||
|
||||
log.info('connecting to {0}:{1} for mongo ext_tops'.format(host, port))
|
||||
conn = pymongo.Connection(host, port)
|
||||
conn = pymongo.MongoClient(host, port)
|
||||
|
||||
log.debug('using database \'{0}\''.format(__opts__['mongo.db']))
|
||||
mdb = conn[__opts__['mongo.db']]
|
||||
@ -127,7 +127,7 @@ def top(**kwargs):
|
||||
)
|
||||
)
|
||||
|
||||
result = mdb[collection].find_one({id_field: minion_id}, fields=[states_field, environment_field])
|
||||
result = mdb[collection].find_one({id_field: minion_id}, projection=[states_field, environment_field])
|
||||
if result and states_field in result:
|
||||
if environment_field in result:
|
||||
environment = result[environment_field]
|
||||
|
@ -540,6 +540,7 @@ class TestWebhookSaltAPIHandler(SaltnadoTestCase):
|
||||
self.assertIn('headers', event['data'])
|
||||
self.assertEqual(event['data']['post'], {'foo': 'bar'})
|
||||
# get an event future
|
||||
self._finished = False # TODO: remove after some cleanup of the event listener
|
||||
event = self.application.event_listener.get_event(self,
|
||||
tag='salt/netapi/hook',
|
||||
callback=verify_event,
|
||||
|
@ -37,7 +37,7 @@ RET = [{'created': '2014-07-25',
|
||||
|
||||
class Mockgnupg(object):
|
||||
'''
|
||||
Mock smtplib class
|
||||
Mock gnupg class
|
||||
'''
|
||||
__version__ = '1.3.1'
|
||||
fingerprint = u'F321F'
|
||||
@ -54,7 +54,7 @@ class Mockgnupg(object):
|
||||
|
||||
class GPG(object):
|
||||
'''
|
||||
Mock smtplib class
|
||||
Mock gnupg class
|
||||
'''
|
||||
def __init__(self, gnupghome='/tmp/salt/.gnupg',
|
||||
homedir='/tmp/salt/.gnupg'):
|
||||
|
474
tests/unit/modules/redismod_test.py
Normal file
474
tests/unit/modules/redismod_test.py
Normal file
@ -0,0 +1,474 @@
|
||||
# -*- 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.modules import redismod
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# Globals
|
||||
redismod.__grains__ = {}
|
||||
redismod.__salt__ = {}
|
||||
redismod.__context__ = {}
|
||||
redismod.__opts__ = {}
|
||||
|
||||
|
||||
class Mockredis(object):
|
||||
'''
|
||||
Mock redis class
|
||||
'''
|
||||
class ConnectionError(Exception):
|
||||
'''
|
||||
Mock ConnectionError class
|
||||
'''
|
||||
pass
|
||||
|
||||
redismod.redis = Mockredis
|
||||
|
||||
|
||||
class MockConnect(object):
|
||||
'''
|
||||
Mock Connect class
|
||||
'''
|
||||
counter = 0
|
||||
|
||||
def __init__(self):
|
||||
self.name = None
|
||||
self.pattern = None
|
||||
self.value = None
|
||||
self.key = None
|
||||
self.seconds = None
|
||||
self.timestamp = None
|
||||
self.field = None
|
||||
self.start = None
|
||||
self.stop = None
|
||||
self.master_host = None
|
||||
self.master_port = None
|
||||
|
||||
@staticmethod
|
||||
def bgrewriteaof():
|
||||
'''
|
||||
Mock bgrewriteaof method
|
||||
'''
|
||||
return 'A'
|
||||
|
||||
@staticmethod
|
||||
def bgsave():
|
||||
'''
|
||||
Mock bgsave method
|
||||
'''
|
||||
return 'A'
|
||||
|
||||
def config_get(self, pattern):
|
||||
'''
|
||||
Mock config_get method
|
||||
'''
|
||||
self.pattern = pattern
|
||||
return 'A'
|
||||
|
||||
def config_set(self, name, value):
|
||||
'''
|
||||
Mock config_set method
|
||||
'''
|
||||
self.name = name
|
||||
self.value = value
|
||||
return 'A'
|
||||
|
||||
@staticmethod
|
||||
def dbsize():
|
||||
'''
|
||||
Mock dbsize method
|
||||
'''
|
||||
return 'A'
|
||||
|
||||
@staticmethod
|
||||
def delete():
|
||||
'''
|
||||
Mock delete method
|
||||
'''
|
||||
return 'A'
|
||||
|
||||
def exists(self, key):
|
||||
'''
|
||||
Mock exists method
|
||||
'''
|
||||
self.key = key
|
||||
return 'A'
|
||||
|
||||
def expire(self, key, seconds):
|
||||
'''
|
||||
Mock expire method
|
||||
'''
|
||||
self.key = key
|
||||
self.seconds = seconds
|
||||
return 'A'
|
||||
|
||||
def expireat(self, key, timestamp):
|
||||
'''
|
||||
Mock expireat method
|
||||
'''
|
||||
self.key = key
|
||||
self.timestamp = timestamp
|
||||
return 'A'
|
||||
|
||||
@staticmethod
|
||||
def flushall():
|
||||
'''
|
||||
Mock flushall method
|
||||
'''
|
||||
return 'A'
|
||||
|
||||
@staticmethod
|
||||
def flushdb():
|
||||
'''
|
||||
Mock flushdb method
|
||||
'''
|
||||
return 'A'
|
||||
|
||||
def get(self, key):
|
||||
'''
|
||||
Mock get method
|
||||
'''
|
||||
self.key = key
|
||||
return 'A'
|
||||
|
||||
def hget(self, key, field):
|
||||
'''
|
||||
Mock hget method
|
||||
'''
|
||||
self.key = key
|
||||
self.field = field
|
||||
return 'A'
|
||||
|
||||
def hgetall(self, key):
|
||||
'''
|
||||
Mock hgetall method
|
||||
'''
|
||||
self.key = key
|
||||
return 'A'
|
||||
|
||||
@staticmethod
|
||||
def info():
|
||||
'''
|
||||
Mock info method
|
||||
'''
|
||||
return 'A'
|
||||
|
||||
def keys(self, pattern):
|
||||
'''
|
||||
Mock keys method
|
||||
'''
|
||||
self.pattern = pattern
|
||||
return 'A'
|
||||
|
||||
def type(self, key):
|
||||
'''
|
||||
Mock type method
|
||||
'''
|
||||
self.key = key
|
||||
return 'A'
|
||||
|
||||
@staticmethod
|
||||
def lastsave():
|
||||
'''
|
||||
Mock lastsave method
|
||||
'''
|
||||
return datetime.now()
|
||||
|
||||
def llen(self, key):
|
||||
'''
|
||||
Mock llen method
|
||||
'''
|
||||
self.key = key
|
||||
return 'A'
|
||||
|
||||
def lrange(self, key, start, stop):
|
||||
'''
|
||||
Mock lrange method
|
||||
'''
|
||||
self.key = key
|
||||
self.start = start
|
||||
self.stop = stop
|
||||
return 'A'
|
||||
|
||||
@staticmethod
|
||||
def ping():
|
||||
'''
|
||||
Mock ping method
|
||||
'''
|
||||
MockConnect.counter = MockConnect.counter + 1
|
||||
if MockConnect.counter == 1:
|
||||
return 'A'
|
||||
elif MockConnect.counter in (2, 3, 5):
|
||||
raise Mockredis.ConnectionError('foo')
|
||||
|
||||
@staticmethod
|
||||
def save():
|
||||
'''
|
||||
Mock save method
|
||||
'''
|
||||
return 'A'
|
||||
|
||||
def set(self, key, value):
|
||||
'''
|
||||
Mock set method
|
||||
'''
|
||||
self.key = key
|
||||
self.value = value
|
||||
return 'A'
|
||||
|
||||
@staticmethod
|
||||
def shutdown():
|
||||
'''
|
||||
Mock shutdown method
|
||||
'''
|
||||
return 'A'
|
||||
|
||||
def slaveof(self, master_host, master_port):
|
||||
'''
|
||||
Mock slaveof method
|
||||
'''
|
||||
self.master_host = master_host
|
||||
self.master_port = master_port
|
||||
return 'A'
|
||||
|
||||
def smembers(self, key):
|
||||
'''
|
||||
Mock smembers method
|
||||
'''
|
||||
self.key = key
|
||||
return 'A'
|
||||
|
||||
@staticmethod
|
||||
def time():
|
||||
'''
|
||||
Mock time method
|
||||
'''
|
||||
return 'A'
|
||||
|
||||
def zcard(self, key):
|
||||
'''
|
||||
Mock zcard method
|
||||
'''
|
||||
self.key = key
|
||||
return 'A'
|
||||
|
||||
def zrange(self, key, start, stop):
|
||||
'''
|
||||
Mock zrange method
|
||||
'''
|
||||
self.key = key
|
||||
self.start = start
|
||||
self.stop = stop
|
||||
return 'A'
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
@patch('salt.modules.redismod._connect', MagicMock(return_value=MockConnect()))
|
||||
class RedismodTestCase(TestCase):
|
||||
'''
|
||||
Test cases for salt.modules.redismod
|
||||
'''
|
||||
def test_bgrewriteaof(self):
|
||||
'''
|
||||
Test to asynchronously rewrite the append-only file
|
||||
'''
|
||||
self.assertEqual(redismod.bgrewriteaof(), 'A')
|
||||
|
||||
def test_bgsave(self):
|
||||
'''
|
||||
Test to asynchronously save the dataset to disk
|
||||
'''
|
||||
self.assertEqual(redismod.bgsave(), 'A')
|
||||
|
||||
def test_config_get(self):
|
||||
'''
|
||||
Test to get redis server configuration values
|
||||
'''
|
||||
self.assertEqual(redismod.config_get('*'), 'A')
|
||||
|
||||
def test_config_set(self):
|
||||
'''
|
||||
Test to set redis server configuration values
|
||||
'''
|
||||
self.assertEqual(redismod.config_set('name', 'value'), 'A')
|
||||
|
||||
def test_dbsize(self):
|
||||
'''
|
||||
Test to return the number of keys in the selected database
|
||||
'''
|
||||
self.assertEqual(redismod.dbsize(), 'A')
|
||||
|
||||
def test_delete(self):
|
||||
'''
|
||||
Test to deletes the keys from redis, returns number of keys deleted
|
||||
'''
|
||||
self.assertEqual(redismod.delete(), 'A')
|
||||
|
||||
def test_exists(self):
|
||||
'''
|
||||
Test to return true if the key exists in redis
|
||||
'''
|
||||
self.assertEqual(redismod.exists('key'), 'A')
|
||||
|
||||
def test_expire(self):
|
||||
'''
|
||||
Test to set a keys time to live in seconds
|
||||
'''
|
||||
self.assertEqual(redismod.expire('key', 'seconds'), 'A')
|
||||
|
||||
def test_expireat(self):
|
||||
'''
|
||||
Test to set a keys expire at given UNIX time
|
||||
'''
|
||||
self.assertEqual(redismod.expireat('key', 'timestamp'), 'A')
|
||||
|
||||
def test_flushall(self):
|
||||
'''
|
||||
Test to remove all keys from all databases
|
||||
'''
|
||||
self.assertEqual(redismod.flushall(), 'A')
|
||||
|
||||
def test_flushdb(self):
|
||||
'''
|
||||
Test to remove all keys from the selected database
|
||||
'''
|
||||
self.assertEqual(redismod.flushdb(), 'A')
|
||||
|
||||
def test_get_key(self):
|
||||
'''
|
||||
Test to get redis key value
|
||||
'''
|
||||
self.assertEqual(redismod.get_key('key'), 'A')
|
||||
|
||||
def test_hget(self):
|
||||
'''
|
||||
Test to get specific field value from a redis hash, returns dict
|
||||
'''
|
||||
self.assertEqual(redismod.hget('key', 'field'), 'A')
|
||||
|
||||
def test_hgetall(self):
|
||||
'''
|
||||
Test to get all fields and values from a redis hash, returns dict
|
||||
'''
|
||||
self.assertEqual(redismod.hgetall('key'), 'A')
|
||||
|
||||
def test_info(self):
|
||||
'''
|
||||
Test to get information and statistics about the server
|
||||
'''
|
||||
self.assertEqual(redismod.info(), 'A')
|
||||
|
||||
def test_keys(self):
|
||||
'''
|
||||
Test to get redis keys, supports glob style patterns
|
||||
'''
|
||||
self.assertEqual(redismod.keys('pattern'), 'A')
|
||||
|
||||
def test_key_type(self):
|
||||
'''
|
||||
Test to get redis key type
|
||||
'''
|
||||
self.assertEqual(redismod.key_type('key'), 'A')
|
||||
|
||||
def test_lastsave(self):
|
||||
'''
|
||||
Test to get the UNIX time in seconds of the last successful
|
||||
save to disk
|
||||
'''
|
||||
self.assertTrue(redismod.lastsave())
|
||||
|
||||
def test_llen(self):
|
||||
'''
|
||||
Test to get the length of a list in Redis
|
||||
'''
|
||||
self.assertEqual(redismod.llen('key'), 'A')
|
||||
|
||||
def test_lrange(self):
|
||||
'''
|
||||
Test to get a range of values from a list in Redis
|
||||
'''
|
||||
self.assertEqual(redismod.lrange('key', 'start', 'stop'), 'A')
|
||||
|
||||
def test_ping(self):
|
||||
'''
|
||||
Test to ping the server, returns False on connection errors
|
||||
'''
|
||||
self.assertEqual(redismod.ping(), 'A')
|
||||
|
||||
self.assertFalse(redismod.ping())
|
||||
|
||||
def test_save(self):
|
||||
'''
|
||||
Test to synchronously save the dataset to disk
|
||||
'''
|
||||
self.assertEqual(redismod.save(), 'A')
|
||||
|
||||
def test_set_key(self):
|
||||
'''
|
||||
Test to set redis key value
|
||||
'''
|
||||
self.assertEqual(redismod.set_key('key', 'value'), 'A')
|
||||
|
||||
def test_shutdown(self):
|
||||
'''
|
||||
Test to synchronously save the dataset to disk and then
|
||||
shut down the server
|
||||
'''
|
||||
self.assertFalse(redismod.shutdown())
|
||||
|
||||
self.assertTrue(redismod.shutdown())
|
||||
|
||||
self.assertFalse(redismod.shutdown())
|
||||
|
||||
def test_slaveof(self):
|
||||
'''
|
||||
Test to make the server a slave of another instance, or
|
||||
promote it as master
|
||||
'''
|
||||
self.assertEqual(redismod.slaveof('master_host', 'master_port'), 'A')
|
||||
|
||||
def test_smembers(self):
|
||||
'''
|
||||
Test to get members in a Redis set
|
||||
'''
|
||||
self.assertListEqual(redismod.smembers('key'), ['A'])
|
||||
|
||||
def test_time(self):
|
||||
'''
|
||||
Test to return the current server UNIX time in seconds
|
||||
'''
|
||||
self.assertEqual(redismod.time(), 'A')
|
||||
|
||||
def test_zcard(self):
|
||||
'''
|
||||
Test to get the length of a sorted set in Redis
|
||||
'''
|
||||
self.assertEqual(redismod.zcard('key'), 'A')
|
||||
|
||||
def test_zrange(self):
|
||||
'''
|
||||
Test to get a range of values from a sorted set in Redis by index
|
||||
'''
|
||||
self.assertEqual(redismod.zrange('key', 'start', 'stop'), 'A')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(RedismodTestCase, needs_daemon=False)
|
102
tests/unit/states/supervisord_test.py
Normal file
102
tests/unit/states/supervisord_test.py
Normal file
@ -0,0 +1,102 @@
|
||||
# -*- 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 supervisord
|
||||
|
||||
supervisord.__salt__ = {}
|
||||
supervisord.__opts__ = {}
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class SupervisordTestCase(TestCase):
|
||||
'''
|
||||
Test cases for salt.states.supervisord
|
||||
'''
|
||||
# 'running' function tests: 1
|
||||
|
||||
def test_running(self):
|
||||
'''
|
||||
Test to ensure the named service is running.
|
||||
'''
|
||||
name = 'wsgi_server'
|
||||
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'result': True,
|
||||
'comment': ''}
|
||||
|
||||
comt = ('Supervisord module not activated.'
|
||||
' Do you need to install supervisord?')
|
||||
ret.update({'comment': comt, 'result': False})
|
||||
self.assertDictEqual(supervisord.running(name), ret)
|
||||
|
||||
mock = MagicMock(return_value={name: {'state': 'running'}})
|
||||
with patch.dict(supervisord.__salt__, {'supervisord.status': mock}):
|
||||
with patch.dict(supervisord.__opts__, {'test': True}):
|
||||
comt = ('Service wsgi_server is already running')
|
||||
ret.update({'comment': comt, 'result': True})
|
||||
self.assertDictEqual(supervisord.running(name), ret)
|
||||
|
||||
with patch.dict(supervisord.__opts__, {'test': False}):
|
||||
comt = ('Not starting already running service: wsgi_server')
|
||||
ret.update({'comment': comt})
|
||||
self.assertDictEqual(supervisord.running(name), ret)
|
||||
|
||||
# 'dead' function tests: 1
|
||||
|
||||
def test_dead(self):
|
||||
'''
|
||||
Test to ensure the named service is dead (not running).
|
||||
'''
|
||||
name = 'wsgi_server'
|
||||
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'result': None,
|
||||
'comment': ''}
|
||||
|
||||
with patch.dict(supervisord.__opts__, {'test': True}):
|
||||
comt = ('Service {0} is set to be stopped'.format(name))
|
||||
ret.update({'comment': comt})
|
||||
self.assertDictEqual(supervisord.dead(name), ret)
|
||||
|
||||
# 'mod_watch' function tests: 1
|
||||
|
||||
def test_mod_watch(self):
|
||||
'''
|
||||
Test to always restart on watch.
|
||||
'''
|
||||
name = 'wsgi_server'
|
||||
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'result': None,
|
||||
'comment': ''}
|
||||
|
||||
comt = ('Supervisord module not activated.'
|
||||
' Do you need to install supervisord?')
|
||||
ret.update({'comment': comt, 'result': False})
|
||||
self.assertDictEqual(supervisord.mod_watch(name), ret)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(SupervisordTestCase, needs_daemon=False)
|
Loading…
Reference in New Issue
Block a user