depends: |
|
---|---|
optdepends: |
|
client_libraries: | |
setup: |
All steps below are performed on the machine running the Salt Master daemon. Configuration goes into the Master configuration file.
|
configuration: |
All available configuration options are detailed below. These settings configure the CherryPy HTTP server and do not apply when using an external server such as Apache or Nginx.
|
Authentication is performed by passing a session token with each request. Tokens are generated via the Login
URL.
The token may be sent in one of two ways: as a custom header or as a session cookie. The latter is far more convenient for clients that support cookies.
Include a custom header named X-Auth-Token.
For example, using curl:
curl -sSk https://localhost:8000/login \ -H 'Accept: application/x-yaml' \ -d username=saltdev \ -d password=saltdev \ -d eauth=pam
Copy the token
value from the output and include it in subsequent requests:
curl -sSk https://localhost:8000 \ -H 'Accept: application/x-yaml' \ -H 'X-Auth-Token: 697adbdc8fe971d09ae4c2a3add7248859c87079'\ -d client=local \ -d tgt='*' \ -d fun=test.ping
Sent via a cookie. This option is a convenience for HTTP clients that automatically handle cookie support (such as browsers).
For example, using curl:
# Write the cookie file: curl -sSk https://localhost:8000/login \ -c ~/cookies.txt \ -H 'Accept: application/x-yaml' \ -d username=saltdev \ -d password=saltdev \ -d eauth=auto # Read the cookie file: curl -sSk https://localhost:8000 \ -b ~/cookies.txt \ -H 'Accept: application/x-yaml' \ -d client=local \ -d tgt='*' \ -d fun=test.ping
Another example using the requests library in Python:
>>> import requests >>> session = requests.Session() >>> session.post('http://localhost:8000/login', json={ 'username': 'saltdev', 'password': 'saltdev', 'eauth': 'auto', }) <Response [200]> >>> resp = session.post('http://localhost:8000', json=[{ 'client': 'local', 'tgt': '*', 'fun': 'test.arg', 'arg': ['foo', 'bar'], 'kwarg': {'baz': 'Baz!'}, }]) >>> resp.json() {u'return': [{ ...snip... }]}
See also
You can bypass the session handling via the Run
URL.
This interface directly exposes Salt's Python API. Everything possible at the CLI is possible through the Python API. Commands are executed on the Salt Master.
The root URL (/
) is RPC-like in that it accepts instructions in the request body for what Salt functions to execute, and the response contains the result of those function calls.
For example:
% curl -sSi https://localhost:8000 -H 'Content-type: application/json' -d '[{ "client": "local", "tgt": "*", "fun": "test.ping" }]' HTTP/1.1 200 OK Content-Type: application/json [...snip...] {"return": [{"jerry": true}]}
The request body must be an array of commands. Use this workflow to build a command:
The client
field is a reference to the main Python classes used in Salt's Python API. Read the full Client APIs documentation, but in short:
LocalClient
which sends commands to Minions. Equivalent to the salt
CLI command.RunnerClient
which invokes runner modules on the Master. Equivalent to the salt-run
CLI command.WheelClient
which invokes wheel modules on the Master. Wheel modules do not have a direct CLI equivalent but they typically manage Master-side resources such as state files, pillar files, the Salt config files, and the key wheel module
exposes similar functionality as the salt-key
CLI command.Most clients have variants like synchronous or asynchronous execution as well as others like batch execution. See the full list of client interfaces.
Each client requires different arguments and sometimes has different syntax. For example, LocalClient
requires the tgt
argument because it forwards the command to Minions and the other client interfaces do not. LocalClient
also takes arg
(array) and kwarg
(dictionary) arguments because these values are sent to the Minions and used to execute the requested function there. RunnerClient
and WheelClient
are executed directly on the Master and thus do not need or accept those arguments.
Read the method signatures in the client documentation linked above, but hopefully an example will help illustrate the concept. This example causes Salt to execute two functions -- the test.arg execution function
using LocalClient
and the test.arg
runner function
using RunnerClient
; note the different structure for each command. The results for both are combined and returned as one response.
% curl -b ~/cookies.txt -sSi localhost:8000 -H 'Content-type: application/json' -d ' [ { "client": "local", "tgt": "*", "fun": "test.arg", "arg": ["positional arg one", "positional arg two"], "kwarg": { "keyword arg one": "Hello from a minion", "keyword arg two": "Hello again from a minion" } }, { "client": "runner", "fun": "test.arg", "keyword arg one": "Hello from a master", "keyword arg two": "Runners do not support positional args" } ] ' HTTP/1.1 200 OK [...snip...] { "return": [ { "jerry": { "args": [ "positional arg one", "positional arg two" ], "kwargs": { "keyword arg one": "Hello from a minion", "keyword arg two": "Hello again from a minion", [...snip...] } }, [...snip; other minion returns here...] }, { "args": [], "kwargs": { "keyword arg two": "Runners do not support positional args", "keyword arg one": "Hello from a master" } } ] }
One more example, this time with more commonly used functions:
curl -b /tmp/cookies.txt -sSi localhost:8000 -H 'Content-type: application/json' -d ' [ { "client": "local", "tgt": "*", "fun": "state.sls", "kwarg": { "mods": "apache", "pillar": { "lookup": { "wwwdir": "/srv/httpd/htdocs" } } } }, { "client": "runner", "fun": "cloud.create", "provider": "my-ec2-provider", "instances": "my-centos-6", "image": "ami-1624987f", "delvol_on_destroy", true } ] ' HTTP/1.1 200 OK [...snip...] { "return": [ { "jerry": { "pkg_|-install_apache_|-httpd_|-installed": { [...snip full state return here...] } } [...snip other minion returns here...] }, { [...snip full salt-cloud output here...] } ] }
This REST interface is flexible in what data formats it will accept as well as what formats it will return (e.g., JSON, YAML, urlencoded).
We recommend the JSON format for most HTTP requests. urlencoded data is simple and cannot express complex data structures -- and that is often required for some Salt commands, such as starting a state run that uses Pillar data. Salt's CLI tool can reformat strings passed in at the CLI into complex data structures, and that behavior also works via salt-api, but that can be brittle and since salt-api can accept JSON it is best just to send JSON.
Here is an example of sending urlencoded data:
curl -sSik https://localhost:8000 \ -b ~/cookies.txt \ -d client=runner \ -d fun='jobs.lookup_jid' \ -d jid='20150129182456704682'
urlencoded data caveats
Only a single command may be sent per HTTP request.
Repeating the arg
parameter multiple times will cause those parameters to be combined into a single list.
Note, some popular frameworks and languages (notably jQuery, PHP, and Ruby on Rails) will automatically append empty brackets onto repeated query string parameters. E.g., ?foo[]=fooone&foo[]=footwo
. This is not supported; send ?foo=fooone&foo=footwo
instead, or send JSON or YAML.
A note about curl
The -d
flag to curl does not automatically urlencode data which can affect passwords and other data that contains characters that must be encoded. Use the --data-urlencode
flag instead. E.g.:
curl -ksi http://localhost:8000/login \
-H "Accept: application/json" \
-d username='myapiuser' \
--data-urlencode password='1234+' \
-d eauth='pam'
This module provides a thin wrapper around Salt's Python API. Executing a Salt command via rest_cherrypy is directly analogous to executing a Salt command via Salt's CLI (which also uses the Python API) -- they share the same semantics, performance characteristics, and 98% of the same code. As a rule-of-thumb: if you wouldn't do it at the CLI don't do it via this API.
The CherryPy server is a production-ready, threading HTTP server written in Python. Because it makes use of a thread pool to process HTTP requests it is not ideally suited to maintaining large numbers of concurrent, synchronous connections. On moderate hardware with default settings it should top-out at around 30 to 50 concurrent connections.
That number of long-running, synchronous Salt processes is also not ideal. Like at the CLI, each Salt command run will start a process that instantiates its own LocalClient
, which instantiates its own listener to the Salt event bus, and sends out its own periodic saltutil.find_job
queries to determine if a Minion is still running the command. Not exactly a lightweight operation.
In addition to the above resource overhead for long-running connections, there are the usual HTTP timeout semantics for the CherryPy server, any HTTP client being used, as well as any hardware in between such as proxies, gateways, or load balancers. rest_cherrypy can be configured not to time-out long responses via the expire_responses
setting, and both LocalClient
and RunnerClient
have their own timeout parameters that may be passed as top-level keywords:
curl -b /tmp/cookies.txt -sSi localhost:8000 -H 'Content-type: application/json' -d ' [ { "client": "local", "tgt": "*", "fun": "test.sleep", "kwarg": {"length": 30}, "timeout": 60 }, { "client": "runner", "fun": "test.sleep", "kwarg": {"s_time": 30}, "timeout": 60 } ] '
Given the performance overhead and HTTP timeouts for long-running operations described above, the most effective and most scalable way to use both Salt and salt-api is to run commands asynchronously using the local_async
, runner_async
, and wheel_async
clients.
Running asynchronous jobs results in being able to process 3x more commands per second for LocalClient
and 17x more commands per second for RunnerClient
, in addition to much less network traffic and memory requirements. Job returns can be fetched from Salt's job cache via the /jobs/<jid>
endpoint, or they can be collected into a data store using Salt's Returner system.
The /events
endpoint is specifically designed to handle long-running HTTP connections and it exposes Salt's event bus which includes job returns. Watching this endpoint first, then executing asynchronous Salt commands second, is the most lightweight and scalable way to use rest_cherrypy
while still receiving job returns in real-time. But this requires clients that can properly handle the inherent asynchronicity of that workflow.
The thread_pool
and socket_queue_size
settings can be used to increase the capacity of rest_cherrypy to handle incoming requests. Keep an eye on RAM usage as well as available file handles while testing changes to these settings. As salt-api is a thin wrapper around Salt's Python API, also keep an eye on the performance of Salt when testing.
Now that Salt uses the Tornado concurrency library internally, we plan to improve performance in the API by taking advantage of existing processes and event listeners and to use lightweight coroutines to facilitate more simultaneous HTTP connections and better support for synchronous operations. That effort can be tracked in issue 26505, but until that issue is closed rest_cherrypy will remain the officially recommended REST API.
The rest_cherrypy
netapi module is a standard Python WSGI app. It can be deployed one of two ways.
The default configuration is to run this module using salt-api to start the Python-based CherryPy server. This server is lightweight, multi-threaded, encrypted with SSL, and should be considered production-ready. See the section above for performance expectations.
This module may be deployed on any WSGI-compliant server such as Apache with mod_wsgi or Nginx with FastCGI, to name just two (there are many).
Note, external WSGI servers handle URLs, paths, and SSL certs directly. The rest_cherrypy
configuration options are ignored and the salt-api
daemon does not need to be running at all. Remember Salt authentication credentials are sent in the clear unless SSL is being enforced!
An example Apache virtual host configuration:
<VirtualHost *:80> ServerName example.com ServerAlias *.example.com ServerAdmin [email protected] LogLevel warn ErrorLog /var/www/example.com/logs/error.log CustomLog /var/www/example.com/logs/access.log combined DocumentRoot /var/www/example.com/htdocs WSGIScriptAlias / /path/to/salt/netapi/rest_cherrypy/wsgi.py </VirtualHost>
/
salt.netapi.rest_cherrypy.app.
LowDataAdapter
The primary entry point to Salt's REST API
GET
()
An explanation of the API with links of where to go next
GET
/
Request Headers: | |
---|---|
| |
Status Codes: |
|
Example request:
curl -i localhost:8000
GET / HTTP/1.1 Host: localhost:8000 Accept: application/json
Example response:
HTTP/1.1 200 OK Content-Type: application/json
POST
Mock out specified imports.
This allows autodoc to do its thing without having oodles of req'd installed libs. This doesn't work with import *
imports.
This Mock class can be configured to return a specific values at specific names, if required.
/login
salt.netapi.rest_cherrypy.app.
Login
(*args, **kwargs)
Log in to receive a session token
GET
()
Present the login interface
GET
/login
An explanation of how to log in.
Status Codes: |
|
---|
Example request:
curl -i localhost:8000/login
GET /login HTTP/1.1 Host: localhost:8000 Accept: text/html
Example response:
HTTP/1.1 200 OK Content-Type: text/html
POST
(**kwargs)
Authenticate against Salt's eauth system
POST
/login
Request Headers: | |
---|---|
| |
Form Parameters: | |
| |
Status Codes: |
|
Example request:
curl -si localhost:8000/login \ -c ~/cookies.txt \ -H "Accept: application/json" \ -H "Content-type: application/json" \ -d '{ "username": "saltuser", "password": "saltuser", "eauth": "auto" }'
POST / HTTP/1.1 Host: localhost:8000 Content-Length: 42 Content-Type: application/json Accept: application/json {"username": "saltuser", "password": "saltuser", "eauth": "auto"}
Example response:
HTTP/1.1 200 OK Content-Type: application/json Content-Length: 206 X-Auth-Token: 6d1b722e Set-Cookie: session_id=6d1b722e; expires=Sat, 17 Nov 2012 03:23:52 GMT; Path=/ {"return": { "token": "6d1b722e", "start": 1363805943.776223, "expire": 1363849143.776224, "user": "saltuser", "eauth": "pam", "perms": [ "grains.*", "status.*", "sys.*", "test.*" ] }}
/logout
salt.netapi.rest_cherrypy.app.
Logout
Class to remove or invalidate sessions
POST
()
Destroy the currently active session and expire the session cookie
/minions
salt.netapi.rest_cherrypy.app.
Minions
Convenience URLs for working with minions
GET
(mid=None)
A convenience URL for getting lists of minions or getting minion details
GET
/minions/
(mid)
Request Headers: | |
---|---|
| |
Status Codes: |
|
Example request:
curl -i localhost:8000/minions/ms-3
GET /minions/ms-3 HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml
Example response:
HTTP/1.1 200 OK Content-Length: 129005 Content-Type: application/x-yaml return: - ms-3: grains.items: ...
POST
(**kwargs)
Start an execution command and immediately return the job id
POST
/minions
Request Headers: | |
---|---|
| |
Response Headers: | |
| |
Status Codes: |
|
Lowstate data describing Salt commands must be sent in the request body. The client
option will be set to local_async()
.
Example request:
curl -sSi localhost:8000/minions \ -b ~/cookies.txt \ -H "Accept: application/x-yaml" \ -d '[{"tgt": "*", "fun": "status.diskusage"}]'
POST /minions HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml Content-Type: application/json tgt=*&fun=status.diskusage
Example response:
HTTP/1.1 202 Accepted Content-Length: 86 Content-Type: application/x-yaml return: - jid: '20130603122505459265' minions: [ms-4, ms-3, ms-2, ms-1, ms-0] _links: jobs: - href: /jobs/20130603122505459265
/jobs
salt.netapi.rest_cherrypy.app.
Jobs
GET
(jid=None, timeout='')
A convenience URL for getting lists of previously run jobs or getting the return from a single job
GET
/jobs/
(jid)
List jobs or show a single job from the job cache.
Request Headers: | |
---|---|
| |
Status Codes: |
|
Example request:
curl -i localhost:8000/jobs
GET /jobs HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml
Example response:
HTTP/1.1 200 OK Content-Length: 165 Content-Type: application/x-yaml return: - '20121130104633606931': Arguments: - '3' Function: test.fib Start Time: 2012, Nov 30 10:46:33.606931 Target: jerry Target-type: glob
Example request:
curl -i localhost:8000/jobs/20121130104633606931
GET /jobs/20121130104633606931 HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml
Example response:
HTTP/1.1 200 OK Content-Length: 73 Content-Type: application/x-yaml info: - Arguments: - '3' Function: test.fib Minions: - jerry Start Time: 2012, Nov 30 10:46:33.606931 Target: '*' Target-type: glob User: saltdev jid: '20121130104633606931' return: - jerry: - - 0 - 1 - 1 - 2 - 6.9141387939453125e-06
/run
salt.netapi.rest_cherrypy.app.
Run
Run commands bypassing the normal session handling
salt-api does not enforce authorization, Salt's eauth system does that. Local/Runner/WheelClient all accept username
/password
/eauth
or token
kwargs that are then checked by the eauth system. The session mechanism in rest_cherrypy
simply pairs a session with a Salt eauth token and then passes the token
kwarg in automatically.
If you already have a Salt eauth token, perhaps generated by the mk_token
function in the Auth Runner module, then there is no reason to use sessions.
This endpoint accepts either a username
, password
, eauth
trio, or a token
kwarg and does not make use of sessions at all.
POST
(**kwargs)
Run commands bypassing the normal session handling Other than that this URL is identical to the root URL (/)
.
POST
/run
An array of lowstate data describing Salt commands must be sent in the request body.
Status Codes: |
|
---|
Example request:
curl -sS localhost:8000/run \ -H 'Accept: application/x-yaml' \ -H 'Content-type: application/json' \ -d '[{ "client": "local", "tgt": "*", "fun": "test.ping", "username": "saltdev", "password": "saltdev", "eauth": "auto" }]'
Or using a Salt Eauth token:
curl -sS localhost:8000/run \ -H 'Accept: application/x-yaml' \ -H 'Content-type: application/json' \ -d '[{ "client": "local", "tgt": "*", "fun": "test.ping", "token": "<salt eauth token here>" }]'
POST /run HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml Content-Length: 75 Content-Type: application/json [{"client": "local", "tgt": "*", "fun": "test.ping", "username": "saltdev", "password": "saltdev", "eauth": "auto"}]
Example response:
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
The /run enpoint can also be used to issue commands using the salt-ssh subsystem.
When using salt-ssh, eauth credentials should not be supplied. Instead, authentication should be handled by the SSH layer itself. The use of the salt-ssh client does not require a salt master to be running. Instead, only a roster file must be present in the salt configuration directory.
All SSH client requests are synchronous.
Example SSH client request:
curl -sS localhost:8000/run \ -H 'Accept: application/x-yaml' \ -d client='ssh' \ -d tgt='*' \ -d fun='test.ping'
POST /run HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml Content-Length: 75 Content-Type: application/x-www-form-urlencoded client=ssh&tgt=*&fun=test.ping
Example SSH response:
return: - silver: fun: test.ping fun_args: [] id: silver jid: '20141203103525666185' retcode: 0 return: true success: true
/events
salt.netapi.rest_cherrypy.app.
Events
Expose the Salt event bus
The event bus on the Salt master exposes a large variety of things, notably when executions are started on the master and also when minions ultimately return their results. This URL provides a real-time window into a running Salt infrastructure.
See also
GET
(token=None, salt_token=None)
An HTTP stream of the Salt master event bus
This stream is formatted per the Server Sent Events (SSE) spec. Each event is formatted as JSON.
GET
/events
Status Codes: |
|
---|---|
Query Parameters: | |
|
Example request:
curl -NsS localhost:8000/events
GET /events HTTP/1.1 Host: localhost:8000
Example response:
Note, the tag
field is not part of the spec. SSE compliant clients should ignore unknown fields. This addition allows non-compliant clients to only watch for certain tags without having to deserialze the JSON object each time.
HTTP/1.1 200 OK Connection: keep-alive Cache-Control: no-cache Content-Type: text/event-stream;charset=utf-8 retry: 400 tag: salt/job/20130802115730568475/new data: {'tag': 'salt/job/20130802115730568475/new', 'data': {'minions': ['ms-4', 'ms-3', 'ms-2', 'ms-1', 'ms-0']}} tag: salt/job/20130802115730568475/ret/jerry data: {'tag': 'salt/job/20130802115730568475/ret/jerry', 'data': {'jid': '20130802115730568475', 'return': True, 'retcode': 0, 'success': True, 'cmd': '_return', 'fun': 'test.ping', 'id': 'ms-1'}}
The event stream can be easily consumed via JavaScript:
var source = new EventSource('/events'); source.onopen = function() { console.info('Listening ...') }; source.onerror = function(err) { console.error(err) }; source.onmessage = function(message) { var saltEvent = JSON.parse(message.data); console.log(saltEvent.tag, saltEvent.data); };
Note, the SSE stream is fast and completely asynchronous and Salt is very fast. If a job is created using a regular POST request, it is possible that the job return will be available on the SSE stream before the response for the POST request arrives. It is important to take that asynchronicity into account when designing an application. Below are some general guidelines.
A full, working proof-of-concept JavaScript application is available adjacent to this file. It can be viewed by pointing a browser at the /app
endpoint in a running rest_cherrypy
instance.
Or using CORS:
var source = new EventSource('/events?token=ecd589e4e01912cf3c4035afad73426dbb8dba75', {withCredentials: true});
It is also possible to consume the stream via the shell.
Records are separated by blank lines; the data:
and tag:
prefixes will need to be removed manually before attempting to unserialize the JSON.
curl's -N
flag turns off input buffering which is required to process the stream incrementally.
Here is a basic example of printing each event as it comes in:
curl -NsS localhost:8000/events |\ while IFS= read -r line ; do echo $line done
Here is an example of using awk to filter events based on tag:
curl -NsS localhost:8000/events |\ awk ' BEGIN { RS=""; FS="\\n" } $1 ~ /^tag: salt\/job\/[0-9]+\/new$/ { print $0 } ' tag: salt/job/20140112010149808995/new data: {"tag": "salt/job/20140112010149808995/new", "data": {"tgt_type": "glob", "jid": "20140112010149808995", "tgt": "jerry", "_stamp": "2014-01-12_01:01:49.809617", "user": "shouse", "arg": [], "fun": "test.ping", "minions": ["jerry"]}} tag: 20140112010149808995 data: {"tag": "20140112010149808995", "data": {"fun_args": [], "jid": "20140112010149808995", "return": true, "retcode": 0, "success": true, "cmd": "_return", "_stamp": "2014-01-12_01:01:49.819316", "fun": "test.ping", "id": "jerry"}}
/hook
salt.netapi.rest_cherrypy.app.
Webhook
A generic web hook entry point that fires an event on Salt's event bus
External services can POST data to this URL to trigger an event in Salt. For example, Amazon SNS, Jenkins-CI or Travis-CI, or GitHub web hooks.
Note
Be mindful of security
Salt's Reactor can run any code. A Reactor SLS that responds to a hook event is responsible for validating that the event came from a trusted source and contains valid data.
This is a generic interface and securing it is up to you!
This URL requires authentication however not all external services can be configured to authenticate. For this reason authentication can be selectively disabled for this URL. Follow best practices -- always use SSL, pass a secret key, configure the firewall to only allow traffic from a known source, etc.
The event data is taken from the request body. The Content-Type header is respected for the payload.
The event tag is prefixed with salt/netapi/hook
and the URL path is appended to the end. For example, a POST
request sent to /hook/mycompany/myapp/mydata
will produce a Salt event with the tag salt/netapi/hook/mycompany/myapp/mydata
.
The following is an example .travis.yml
file to send notifications to Salt of successful test runs:
language: python script: python -m unittest tests after_success: - | curl -sSk https://saltapi-url.example.com:8000/hook/travis/build/success -d branch="${TRAVIS_BRANCH}" -d commit="${TRAVIS_COMMIT}"
See also
POST
(*args, **kwargs)
Fire an event in Salt with a custom event tag and data
POST
/hook
Status Codes: |
|
---|
Example request:
curl -sS localhost:8000/hook \ -H 'Content-type: application/json' \ -d '{"foo": "Foo!", "bar": "Bar!"}'
POST /hook HTTP/1.1 Host: localhost:8000 Content-Length: 16 Content-Type: application/json {"foo": "Foo!", "bar": "Bar!"}
Example response:
HTTP/1.1 200 OK Content-Length: 14 Content-Type: application/json {"success": true}
As a practical example, an internal continuous-integration build server could send an HTTP POST request to the URL https://localhost:8000/hook/mycompany/build/success
which contains the result of a build and the SHA of the version that was built as JSON. That would then produce the following event in Salt that could be used to kick off a deployment via Salt's Reactor:
Event fired at Fri Feb 14 17:40:11 2014 ************************* Tag: salt/netapi/hook/mycompany/build/success Data: {'_stamp': '2014-02-14_17:40:11.440996', 'headers': { 'X-My-Secret-Key': 'F0fAgoQjIT@W', 'Content-Length': '37', 'Content-Type': 'application/json', 'Host': 'localhost:8000', 'Remote-Addr': '127.0.0.1'}, 'post': {'revision': 'aa22a3c4b2e7', 'result': True}}
Salt's Reactor could listen for the event:
reactor: - 'salt/netapi/hook/mycompany/build/*': - /srv/reactor/react_ci_builds.sls
And finally deploy the new build:
{% set secret_key = data.get('headers', {}).get('X-My-Secret-Key') %} {% set build = data.get('post', {}) %} {% if secret_key == 'F0fAgoQjIT@W' and build.result == True %} deploy_my_app: cmd.state.sls: - tgt: 'application*' - arg: - myapp.deploy - kwarg: pillar: revision: {{ revision }} {% endif %}
/keys
salt.netapi.rest_cherrypy.app.
Keys
Convenience URLs for working with minion keys
New in version 2014.7.0.
These URLs wrap the functionality provided by the key wheel
module
functions.
GET
(mid=None)
Show the list of minion keys or detail on a specific key
New in version 2014.7.0.
GET
/keys/
(mid)
List all keys or show a specific key
Request Headers: | |
---|---|
| |
Status Codes: |
|
Example request:
curl -i localhost:8000/keys
GET /keys HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml
Example response:
HTTP/1.1 200 OK Content-Length: 165 Content-Type: application/x-yaml return: local: - master.pem - master.pub minions: - jerry minions_pre: [] minions_rejected: []
Example request:
curl -i localhost:8000/keys/jerry
GET /keys/jerry HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml
Example response:
HTTP/1.1 200 OK Content-Length: 73 Content-Type: application/x-yaml return: minions: jerry: 51:93:b3:d0:9f:3a:6d:e5:28:67:c2:4b:27:d6:cd:2b
POST
(**kwargs)
Easily generate keys for a minion and auto-accept the new key
Accepts all the same parameters as the key.gen_accept
.
Note
A note about curl
Avoid using the -i
flag or HTTP headers will be written and produce an invalid tar file.
Example partial kickstart script to bootstrap a new minion:
%post mkdir -p /etc/salt/pki/minion curl -sSk https://localhost:8000/keys \ -d mid=jerry \ -d username=kickstart \ -d password=kickstart \ -d eauth=pam \ | tar -C /etc/salt/pki/minion -xf - mkdir -p /etc/salt/minion.d printf 'master: 10.0.0.5\nid: jerry' > /etc/salt/minion.d/id.conf %end
POST
/keys
Generate a public and private key and return both as a tarball
Authentication credentials must be passed in the request.
Status Codes: |
|
---|
Example request:
curl -sSk https://localhost:8000/keys \ -d mid=jerry \ -d username=kickstart \ -d password=kickstart \ -d eauth=pam \ -o jerry-salt-keys.tar
POST /keys HTTP/1.1 Host: localhost:8000
Example response:
HTTP/1.1 200 OK Content-Length: 10240 Content-Disposition: attachment; filename="saltkeys-jerry.tar" Content-Type: application/x-tar jerry.pub0000644000000000000000000000070300000000000010730 0ustar 00000000000000
/ws
salt.netapi.rest_cherrypy.app.
WebsocketEndpoint
Open a WebSocket connection to Salt's event bus
The event bus on the Salt master exposes a large variety of things, notably when executions are started on the master and also when minions ultimately return their results. This URL provides a real-time window into a running Salt infrastructure. Uses websocket as the transport mechanism.
See also
GET
(token=None, **kwargs)
Return a websocket connection of Salt's event stream
GET
/ws/
(token)
Query format_events: | |
---|---|
The event stream will undergo server-side formatting if the curl -NsS <...snip...> localhost:8000/ws?format_events | |
Reqheader X-Auth-Token: | |
an authentication token from | |
Status 101: |
switching to the websockets protocol |
Status 401: |
authentication required |
Status 406: |
requested Content-Type not available |
Example request:
curl -NsSk \ -H 'X-Auth-Token: ffedf49d' \ -H 'Host: localhost:8000' \ -H 'Connection: Upgrade' \ -H 'Upgrade: websocket' \ -H 'Origin: https://localhost:8000' \ -H 'Sec-WebSocket-Version: 13' \ -H 'Sec-WebSocket-Key: '"$(echo -n $RANDOM | base64)" \ localhost:8000/ws
GET /ws HTTP/1.1 Connection: Upgrade Upgrade: websocket Host: localhost:8000 Origin: https://localhost:8000 Sec-WebSocket-Version: 13 Sec-WebSocket-Key: s65VsgHigh7v/Jcf4nXHnA== X-Auth-Token: ffedf49d
Example response:
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: mWZjBV9FCglzn1rIKJAxrTFlnJE= Sec-WebSocket-Version: 13
An authentication token may optionally be passed as part of the URL for browsers that cannot be configured to send the authentication header or cookie:
curl -NsS <...snip...> localhost:8000/ws/ffedf49d
The event stream can be easily consumed via JavaScript:
// Note, you must be authenticated! var source = new Websocket('ws://localhost:8000/ws/d0ce6c1a'); source.onerror = function(e) { console.debug('error!', e); }; source.onmessage = function(e) { console.debug(e.data); }; source.send('websocket client ready') source.close();
Or via Python, using the Python module websocket-client for example.
# Note, you must be authenticated! from websocket import create_connection ws = create_connection('ws://localhost:8000/ws/d0ce6c1a') ws.send('websocket client ready') # Look at https://pypi.python.org/pypi/websocket-client/ for more # examples. while listening_to_events: print ws.recv() ws.close()
Above examples show how to establish a websocket connection to Salt and activating real time updates from Salt's event stream by signaling websocket client ready
.
/stats
salt.netapi.rest_cherrypy.app.
Stats
Expose statistics on the running CherryPy server
GET
()
Return a dump of statistics collected from the CherryPy server
GET
/stats
Request Headers: | |
---|---|
| |
Response Headers: | |
| |
Status Codes: |
|
© 2019 SaltStack.
Licensed under the Apache License, Version 2.0.
https://docs.saltstack.com/en/latest/ref/netapi/all/salt.netapi.rest_cherrypy.html