Hello community,

here is the log from the commit of package salt-api for openSUSE:Factory 
checked in at 2013-07-19 17:29:20
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/salt-api (Old)
 and      /work/SRC/openSUSE:Factory/.salt-api.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "salt-api"

Changes:
--------
--- /work/SRC/openSUSE:Factory/salt-api/salt-api.changes        2013-05-16 
11:39:07.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.salt-api.new/salt-api.changes   2013-07-19 
17:29:21.000000000 +0200
@@ -1,0 +2,11 @@
+Thu Jul 18 04:46:39 UTC 2013 - [email protected]
+
+- Update package to 0.8.2
+- Backward incompatible needs salt 0.15.9 or greater
+- Changes to rest_cherrypy:
+       - Fixed issue #87 which caused the Salt master's PID file to be 
overwritten.
+       - Fixed an inconsistency with the return format for the /minions 
convenience URL.
+       - Added a dedicated URL for serving an HTML app
+       - Added dedicated URL for serving static media
+
+-------------------------------------------------------------------

Old:
----
  salt-api-0.8.1.tar.gz

New:
----
  salt-api-0.8.2.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ salt-api.spec ++++++
--- /var/tmp/diff_new_pack.zu8NXM/_old  2013-07-19 17:29:22.000000000 +0200
+++ /var/tmp/diff_new_pack.zu8NXM/_new  2013-07-19 17:29:22.000000000 +0200
@@ -16,7 +16,7 @@
 #
 
 Name:           salt-api
-Version:        0.8.1
+Version:        0.8.2
 Release:        1%{?dist}
 License:        Apache-2.0
 Summary:        The api for Salt a parallel remote execution system
@@ -35,7 +35,7 @@
 
 BuildRequires:  fdupes
 BuildRequires:  python-devel
-BuildRequires: salt
+BuildRequires: salt >= 0.15.9
 BuildRequires: salt-master
 
 Requires:       salt

++++++ salt-api-0.8.1.tar.gz -> salt-api-0.8.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/salt-api-0.8.1/PKG-INFO new/salt-api-0.8.2/PKG-INFO
--- old/salt-api-0.8.1/PKG-INFO 2013-04-22 20:46:24.000000000 +0200
+++ new/salt-api-0.8.2/PKG-INFO 2013-07-17 18:36:01.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: salt-api
-Version: 0.8.1
+Version: 0.8.2
 Summary: Generic interface for providing external access APIs to Salt
 Home-page: http://saltstack.org
 Author: Thomas S Hatch
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/salt-api-0.8.1/doc/topics/releases/0.8.2.rst 
new/salt-api-0.8.2/doc/topics/releases/0.8.2.rst
--- old/salt-api-0.8.1/doc/topics/releases/0.8.2.rst    1970-01-01 
01:00:00.000000000 +0100
+++ new/salt-api-0.8.2/doc/topics/releases/0.8.2.rst    2013-07-17 
18:12:45.000000000 +0200
@@ -0,0 +1,25 @@
+==============
+salt-api 0.8.2
+==============
+
+:program:`salt-api` 0.8.2 is largely a bugfix release that fixes a
+compatibility issue with changes in Salt 0.15.9.
+
+.. note::
+
+    Requires Salt 0.15.9 or greater
+
+The following changes have been made to the :py:mod:`rest_cherrypy
+<saltapi.netapi.rest_cherrypy.app>` netapi module that provides a RESTful
+interface for a running Salt system:
+
+* Fixed issue #87 which caused the Salt master's PID file to be overwritten.
+* Fixed an inconsistency with the return format for the ``/minions``
+  convenience URL.
+
+  .. warning::
+
+        This is a backward incompatible change.
+
+* Added a dedicated URL for serving an HTML app
+* Added dedicated URL for serving static media
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/salt-api-0.8.1/saltapi/cli.py 
new/salt-api-0.8.2/saltapi/cli.py
--- old/salt-api-0.8.1/saltapi/cli.py   2013-02-27 19:49:08.000000000 +0100
+++ new/salt-api-0.8.2/saltapi/cli.py   2013-07-16 01:24:33.000000000 +0200
@@ -1,7 +1,12 @@
 '''
 CLI entry-point for salt-api
 '''
+# Import python libs
+import sys
+import logging
+
 # Import salt libs
+import salt.utils.verify
 from salt.utils.parsers import (
     ConfigDirMixIn,
     DaemonMixIn,
@@ -16,6 +21,8 @@
 import saltapi.config
 import saltapi.version
 
+log = logging.getLogger(__name__)
+
 
 class SaltAPI(OptionParser, ConfigDirMixIn, LogLevelMixIn, PidfileMixin,
               DaemonMixIn, MergeConfigMixIn):
@@ -26,16 +33,35 @@
 
     VERSION = saltapi.version.__version__
 
+    # ConfigDirMixIn config filename attribute
+    _config_filename_ = 'master'
+    # LogLevelMixIn attributes
+    _default_logging_logfile_ = '/var/log/salt/api'
+
     def setup_config(self):
-        return saltapi.config.api_config(self.get_config_file_path('master'))
+        return saltapi.config.api_config(self.get_config_file_path())
 
     def run(self):
         '''
         Run the api
         '''
         self.parse_args()
-        self.process_config_dir()
+        try:
+            if self.config['verify_env']:
+                logfile = self.config['log_file']
+                if logfile is not None and not logfile.startswith('tcp://') \
+                        and not logfile.startswith('udp://') \
+                        and not logfile.startswith('file://'):
+                    # Logfile is not using Syslog, verify
+                    salt.utils.verify.verify_files(
+                        [logfile], self.config['user']
+                    )
+        except OSError as err:
+            log.error(err)
+            sys.exit(err.errno)
+
+        self.setup_logfile_logger()
+        client = saltapi.client.SaltAPIClient(self.config)
         self.daemonize_if_required()
         self.set_pidfile()
-        client = saltapi.client.SaltAPIClient(self.config)
         client.run()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/salt-api-0.8.1/saltapi/config.py 
new/salt-api-0.8.2/saltapi/config.py
--- old/salt-api-0.8.1/saltapi/config.py        2013-03-05 06:46:00.000000000 
+0100
+++ new/salt-api-0.8.2/saltapi/config.py        2013-07-08 19:42:08.000000000 
+0200
@@ -8,7 +8,7 @@
 DEFAULT_API_OPTS = {
     # ----- Salt master settings overridden by Salt-API --------------------->
     'pidfile': '/var/run/salt-api.pid',
-    'logfile': '/var/log/api',
+    'logfile': '/var/log/salt/api',
     # <---- Salt master settings overridden by Salt-API ----------------------
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/salt-api-0.8.1/saltapi/netapi/rest_cherrypy/__init__.py 
new/salt-api-0.8.2/saltapi/netapi/rest_cherrypy/__init__.py
--- old/salt-api-0.8.1/saltapi/netapi/rest_cherrypy/__init__.py 2013-04-10 
21:15:24.000000000 +0200
+++ new/salt-api-0.8.2/saltapi/netapi/rest_cherrypy/__init__.py 2013-07-16 
01:09:36.000000000 +0200
@@ -85,6 +85,12 @@
         from . import wsgi
         application = wsgi.get_application(root, apiopts, conf)
 
+        if not 'ssl_crt' in apiopts or not 'ssl_key' in apiopts:
+            logger.error("Not starting '%s'. Options 'ssl_crt' and 'ssl_key' "
+                    "are required in production mode." % __name__)
+
+            return None
+
         # Mount and start the WSGI app using the production CherryPy server
         verify_certs(apiopts['ssl_crt'], apiopts['ssl_key'])
 
@@ -92,7 +98,7 @@
                 apiopts['ssl_crt'], apiopts['ssl_key'])
         wsgi_d = wsgiserver.WSGIPathInfoDispatcher({'/': application})
         server = wsgiserver.CherryPyWSGIServer(
-                ('0.0.0.0', apiopts['port']),
+                (apiopts.get('host', '0.0.0.0'), apiopts['port']),
                 wsgi_app=wsgi_d)
         server.ssl_adapter = ssl_a
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/salt-api-0.8.1/saltapi/netapi/rest_cherrypy/app.py 
new/salt-api-0.8.2/saltapi/netapi/rest_cherrypy/app.py
--- old/salt-api-0.8.1/saltapi/netapi/rest_cherrypy/app.py      2013-04-22 
04:33:42.000000000 +0200
+++ new/salt-api-0.8.2/saltapi/netapi/rest_cherrypy/app.py      2013-07-17 
18:12:45.000000000 +0200
@@ -2,6 +2,8 @@
 A REST API for Salt
 ===================
 
+.. py:currentmodule:: saltapi.netapi.rest_cherrypy.app
+
 :depends:   - CherryPy Python module
 :configuration: All authentication is done through Salt's :ref:`external auth
     <acl-eauth>` system. Be sure that it is enabled and the user you are
@@ -15,6 +17,10 @@
         **Required**
 
         The port for the webserver to listen on.
+    host : ``0.0.0.0``
+        The socket interface for the HTTP server to listen on.
+
+        .. versionadded:: 0.8.2
     debug : ``False``
         Starts a for-development web server instead of the production-ready web
         server.
@@ -30,14 +36,24 @@
         The path to the private key for your SSL certificate. (See below)
     static
         A filesystem path to static HTML/JavaScript/CSS/image assets.
-        If this directory contains a ``index.html`` file, it will be served at
-        the root URL when HTML is requested by a client via the ``Accept``
-        header.
+    static_path : ``/static``
+        The URL prefix to use when serving static assets out of the directory
+        specified in the ``static`` setting.
+
+        .. versionadded:: 0.8.2
+    app
+        A filesystem path to an HTML file that will be served as a static file.
+        This is useful for bootstrapping a single-page JavaScript app.
+
+        .. versionadded:: 0.8.2
+    app_path : ``/app``
+        The URL prefix to use for serving the HTML file specifed in the ``app``
+        setting. This should be a simple name containing no slashes.
 
-        This directory may point to a clone of the `salt-ui`_ project to
-        bootstrap a graphical interface for interacting with Salt.
+        Any path information after the specified path is ignored; this is
+        useful for apps that utilize the HTML5 history API.
 
-    .. _`salt-ui`: https://github.com/saltstack/salt-ui
+        .. versionadded:: 0.8.2
 
     Example production configuration block:
 
@@ -58,6 +74,18 @@
 
         % salt-call tls.create_self_signed_cert
 
+Authentication
+--------------
+
+Authentication is performed by passing a session token with each request. The
+token may be sent either via a custom header named :mailheader:`X-Auth-Token`
+or sent inside a cookie. (The result is the same but browsers and some HTTP
+clients handle cookies automatically and transparently so it is a convenience.)
+
+Token are generated via the :py:class:`Login` URL.
+
+.. seealso:: You can bypass the session handling via the :py:class:`Run` URL.
+
 Usage
 -----
 
@@ -120,9 +148,10 @@
 URL reference
 -------------
 
-The main entry point is the root URL (``/``) and all functionality is available
-at that URL. The other URLs are largely convenience URLs that wrap that main
-entry point.
+The main entry point is the :py:class:`root URL (/) <LowDataAdapter>` and all
+functionality is available at that URL. The other URLs are largely convenience
+URLs that wrap that main entry point with shorthand or specialized
+functionality.
 
 '''
 # We need a custom pylintrc here...
@@ -178,24 +207,6 @@
     cherrypy.response.headers['Cache-Control'] = 'private'
 
 
-def wants_html():
-    '''
-    Determine if the request is asking for HTML specifically.
-
-    Returns an empty string or a string containing the output of the
-    cherrypy.lib.cptools.accept() function.
-    '''
-    # Short-circuit if the request is vague or overly broad
-    if (not 'Accept' in cherrypy.request.headers
-            or cherrypy.request.headers['Accept'] == '*/*'):
-        return ''
-
-    try:
-        return cherrypy.lib.cptools.accept(
-                ['text/html'] + [i for (i, _) in ct_out_map])
-    except (AttributeError, cherrypy.CherryPyException):
-        return ''
-
 # Be conservative in what you send
 # Maps Content-Type to serialization functions; this is a tuple of tuples to
 # preserve order of preference.
@@ -214,14 +225,6 @@
     :param args: Pass args through to the main handler
     :param kwargs: Pass kwargs through to the main handler
     '''
-    # If we're being asked for HTML, try to serve index.html from the 'static'
-    # directory; this is useful (as a random, non-specific example) for
-    # bootstrapping the salt-ui app
-    if 'static' in cherrypy.config and 'html' in wants_html():
-        index = os.path.join(cherrypy.config['static'], 'index.html')
-        if os.path.exists(index):
-            return cherrypy.lib.static.serve_file(index)
-
     # Execute the real handler. Handle or pass-through any errors we know how
     # to handle (auth & HTTP errors). Reformat any errors we don't know how to
     # handle as a data structure.
@@ -258,6 +261,8 @@
 
 def hypermedia_out():
     '''
+    Determine the best handler for the requested content type
+
     Wrap the normal handler and transform the output from that handler into the
     requested content type
     '''
@@ -265,12 +270,7 @@
     request._hypermedia_inner_handler = request.handler
     request.handler = hypermedia_handler
 
-    # cherrypy.response.headers['Alternates'] = self.ct_out_map.keys()
-    # TODO: add 'negotiate' to Vary header and 'list' to TCN header
-    # Alternates: {"paper.1" 0.9 {type text/html} {language en}},
-    #          {"paper.2" 0.7 {type text/html} {language fr}},
-    #          {"paper.3" 1.0 {type application/postscript} {language en}}
-
+    cherrypy.response.headers['Access-Control-Allow-Origin'] = '*'
 
 
 @functools.wraps
@@ -391,13 +391,9 @@
         'tools.hypermedia_in.on': True,
     }
 
-    def __init__(self, opts):
-        '''
-        :param opts: A dictionary of options from Salt's master config (e.g.
-            Salt's, ``__opts__``)
-        '''
-        self.opts = opts
-        self.api = saltapi.APIClient(opts)
+    def __init__(self):
+        self.opts = cherrypy.config['saltopts']
+        self.api = saltapi.APIClient(self.opts)
 
     def exec_lowstate(self):
         '''
@@ -440,9 +436,20 @@
         :status 401: authentication required
         :status 406: requested Content-Type not available
         '''
+        import inspect
+
+        # Grab all available client interfaces
+        clients = [name for name, _ in inspect.getmembers(saltapi.APIClient,
+            predicate=inspect.ismethod) if not name.startswith('__')]
+        clients.remove('run') # run method calls client interfaces
+
+        # Grab a list of output formats
+        formats = [ctype for ctype, _ in ct_out_map]
+
         return {
-            'status': cherrypy.response.status,
             'return': "Welcome",
+            'clients': clients,
+            'output': formats,
         }
 
     def POST(self, **kwargs):
@@ -586,7 +593,13 @@
 
                 - return:
                     jid: '20130118105423694155'
-                    minions: [ms-4, ms-3, ms-2, ms-1, ms-0]
+
+                return:
+                - jid: '20130603122505459265'
+                  minions: [ms-4, ms-3, ms-2, ms-1, ms-0]
+                _links:
+                  jobs:
+                  - href: /jobs/20130603122505459265
 
         :form lowstate: lowstate data for the
             :py:mod:`~salt.client.LocalClient`; the ``client`` parameter will
@@ -601,12 +614,16 @@
         '''
         for chunk in cherrypy.request.lowstate:
             chunk['client'] = 'local_async'
-        job_data = next(self.exec_lowstate(), {})
+        job_data = list(self.exec_lowstate())
 
         cherrypy.response.status = 202
-        return [{
+        return {
             'return': job_data,
-        }]
+            '_links': {
+                'jobs': [{'href': '/jobs/{0}'.format(i['jid'])}
+                    for i in job_data],
+            },
+        }
 
 
 class Jobs(LowDataAdapter):
@@ -859,9 +876,47 @@
 
         .. versionadded:: 0.8.0
 
-        This entry point is primarily for "one-off" commands. Each request must
-        pass full Salt external authentication credentials. Otherwise this URL
-        is identical to the root (``/``) execution URL.
+        .. http:post:: /run
+
+            This entry point is primarily for "one-off" commands. Each request
+            must pass full Salt authentication credentials. Otherwise this URL
+            is identical to the root (``/``) execution URL.
+
+            **Example request**::
+
+                % curl -sS localhost:8000/run \\
+                    -H 'Accept: application/x-yaml' \\
+                    -d client='local' \\
+                    -d tgt='*' \\
+                    -d fun='test.ping' \\
+                    -d username='saltdev' \\
+                    -d password='saltdev' \\
+                    -d eauth='pam'
+
+            .. code-block:: http
+
+                POST /run HTTP/1.1
+                Host: localhost:8000
+                Accept: application/x-yaml
+                Content-Length: 75
+                Content-Type: application/x-www-form-urlencoded
+
+                
client=local&tgt=*&fun=test.ping&username=saltdev&password=saltdev&eauth=pam
+
+            **Example response**:
+
+            .. code-block:: http
+
+                HTTP/1.1 200 OK
+                Content-Length: 73
+                Content-Type: application/x-yaml
+
+                return:
+                - ms-0: true
+                  ms-1: true
+                  ms-2: true
+                  ms-3: true
+                  ms-4: true
 
         :form lowstate: A list of :term:`lowstate` data appropriate for the
             :ref:`client <client-apis>` specified client interface. Full
@@ -875,6 +930,13 @@
         }
 
 
+class App(object):
+    exposed = True
+    def GET(self, *args):
+        apiopts = cherrypy.config['apiopts']
+        return cherrypy.lib.static.serve_file(apiopts['app'])
+
+
 class API(object):
     '''
     Collect configuration and URL map for building the CherryPy app
@@ -888,34 +950,41 @@
         'jobs': Jobs,
     }
 
-    def __init__(self, opts):
-        self.opts = opts
+    def __init__(self):
+        self.opts = cherrypy.config['saltopts']
+        self.apiopts = cherrypy.config['apiopts']
+
         for url, cls in self.url_map.items():
-            setattr(self, url, cls(self.opts))
+            setattr(self, url, cls())
+
+        if 'app' in self.apiopts:
+            setattr(self, self.apiopts.get('app_path', 'app').lstrip('/'), 
App())
 
-    def get_conf(self, apiopts):
+    def get_conf(self):
         '''
         Combine the CherryPy configuration with the rest_cherrypy config values
         pulled from the master config and return the CherryPy configuration
         '''
         conf = {
             'global': {
-                'server.socket_host': '0.0.0.0',
-                'server.socket_port': apiopts.get('port', 8000),
-                'debug': apiopts.get('debug', False),
+                'server.socket_host': self.apiopts.get('host', '0.0.0.0'),
+                'server.socket_port': self.apiopts.get('port', 8000),
+                'debug': self.apiopts.get('debug', False),
             },
             '/': {
                 'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
 
                 'tools.trailing_slash.on': True,
                 'tools.gzip.on': True,
-
-                'tools.staticdir.on': True if 'static' in apiopts else False,
-                'tools.staticdir.dir': apiopts.get('static', ''),
             },
         }
 
-        conf['global'].update(apiopts)
+        # Serve static media if the directory has been set in the configuration
+        if 'static' in self.apiopts:
+            conf[self.apiopts.get('static_path', '/static')] = {
+                'tools.staticdir.on': True,
+                'tools.staticdir.dir': self.apiopts['static'],
+            }
 
         # Add to global config
         cherrypy.config.update(conf['global'])
@@ -927,11 +996,15 @@
     '''
     Returns a WSGI app and a configuration dictionary
     '''
-    root = API(opts) # cherrypy app
     apiopts = opts.get(__name__.rsplit('.', 2)[-2], {}) # rest_cherrypy opts
 
-    cpyopts = root.get_conf(apiopts) # cherrypy app opts
-    gconf = cpyopts.get('global', {}) # 'global' section of cpyopts
+    # Add Salt and salt-api config options to the main CherryPy config dict
+    cherrypy.config['saltopts'] = opts
+    cherrypy.config['apiopts'] = apiopts
+
+    root = API() # cherrypy app
+
+    cpyopts = root.get_conf() # cherrypy app opts
 
     # Register salt-specific hooks
     cherrypy.tools.salt_token = cherrypy.Tool('on_start_resource',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/salt-api-0.8.1/saltapi/version.py 
new/salt-api-0.8.2/saltapi/version.py
--- old/salt-api-0.8.1/saltapi/version.py       2013-04-22 04:44:55.000000000 
+0200
+++ new/salt-api-0.8.2/saltapi/version.py       2013-07-17 18:13:41.000000000 
+0200
@@ -1,4 +1,4 @@
-__version_info__ = (0, 8, 1)
+__version_info__ = (0, 8, 2)
 __version__ = '.'.join(map(str, __version_info__))
 
 # If we can get a version from Git use that instead, otherwise carry on

-- 
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to