How do you use the 'publish' module in your own module - salt-stack

I need to execute a script on another minion. The best solution seems to be Peer Publishing, but the only documentation I have been able to find only shows how to do it via CLI.
How can I define the following in a module?
salt-call system.example.com publish.publish '*' cmd.run './script_to_run'

You want the salt.client.Caller() API.
#!/usr/bin/env python
import salt.client
salt_call = salt.client.Caller()
salt_call.function('publish.publish', 'web001',
'cmd.run', 'logger "publish.publish success"')
You have to run the above as the salt user (usually root).
Then scoot over to web001 and confirm the message is in /var/log/syslog. Worked for me.

The syntax for the .sls file:
salt-call publish.publish \* cmd.run 'cd /*directory* && ./script_to_run.sh:
cmd.run
Alternative syntax:
execute script on other minion:
cmd.run
- name: salt-call publish.publish \* cmd.run 'cd /*directory* && ./script_to_run.sh
What I specifically did (I needed to execute a command, but only if a published command executed successfully. Which command to publish depends on the role of the minion):
execute script:
cmd.run:
- name: *some shell command here*
- cwd: /*directory*
- require:
- file: *some file here*
{% if 'role_1' in grains['roles'] -%}
- onlyif: salt-call publish.publish \* cmd.run 'cd /*other_directory* && ./script_to_run_A.sh'
{% elif 'role_2' in grains['roles'] -%}
- onlyif: salt-call publish.publish \* cmd.run 'cd /*other_directory* && ./script_to_run_B.sh'
{% endif %}
Remember to enable peer communication in /etc/salt/master under the section 'Peer Publish Settings':
peer:
.*:
- .*
This configuration is not secure, since it enables all minions to execute all commands on fellow minions, but I have not figured out the correct syntax to select minions based on their role yet.
Another note is that it probably would be better to create a custom command containing the cmd.run and then enable only that, since enabling all nodes to execute arbitrary scripts on each other is not secure.
The essence of this answer is the same as Dan Garthwaite's, but what I needed was a solution for a .sls file.

Related

How to use files within salt directory for cmd.run?

I want to run a command (cmd.run) which uses files within the salt directory, for example:
fly_set_{{ pipeline }}:
cmd.run:
- name: |
fly -t ci set-pipeline -p {{ pipeline }} -c pipeline.yml -l credentials.yml
- require:
- cmd: fly_login_{{name}}
- file: /etc/concourse/teams/builds/{{ pipeline }}/pipeline.yml
- file: /etc/concourse/teams/builds/{{ pipeline }}/credentials.yml
Is this the correct approach or do I need to do this first?
file:
managed:
- source: salt://concourse/teams/builds/{{ pipeline }}/pipeline.yml
I'm not sure exactly what are you trying to achieve, but if your command is depending on multiple files the right way to do this would be:
/etc/concourse/teams/builds/{{ pipeline }}/pipeline.yml:
file.managed:
- source: salt://concourse/teams/builds/{{ pipeline }}/pipeline.yml
/etc/concourse/teams/builds/{{ pipeline }}/credentials.yml:
file.managed:
- source: salt://concourse/teams/builds/{{ pipeline }}/credentials.yml
fly_set_{{ pipeline }}:
cmd.run:
- name: |
fly -t ci set-pipeline -p {{ pipeline }} -c pipeline.yml -l credentials.yml
- require:
- file: /etc/concourse/teams/builds/{{ pipeline }}/pipeline.yml
- file: /etc/concourse/teams/builds/{{ pipeline }}/credentials.yml
First two file.managed directive will ensure that both files are present on the minion. The require directive in cmd.run will make sure that files are present on the minion before the execution of the command.

Salt Stack: stall the execution

I'm checking few configuration file, and i want to stop the execution of that script if any configuration file is not available with suitable message.
I'm trying cmd.run to print the message and then exit but that's not working for me
SALTSTACK:
{% if not that config is present %}
cmd.run:
- name:
echo SUITABLE MESSAGE
exit
There is the Failhard option for salt. If you use it as a global option, any failure within a state will terminate the execution of all following states. But you can also use it selectively.
Maybe you could add the Failhard option to your existing states.
If you want to have a specific error message, a 1:1 translation of your example could look like this:
SALTSTACK:
cmd.run:
- name: bash -c test -e /tmp/notexist || (echo "unable to find file" && exit 1)
- failhard: True
It will result in an output like this. The error message is at the end.
local:
----------
ID: SALTSTACK
Function: cmd.run
Name: bash -c test -e /tmp/notexist || (echo "unable to find file" && exit 1)
Result: False
Comment: Command "bash -c test -e /tmp/notexist || (echo "unable to find file" && exit 1)" run
Started: 21:30:34.473132
Duration: 7.353 ms
Changes:
----------
pid:
5499
retcode:
1
stderr:
stdout:
unable to find file
Why would you exit in that manner.
The execution will halt when any of the state fails.
And you can use file.exists state to check if the particular configuration file is present. If not the state will fail. Check salt file state: file.exists.

restrict commands that salt-minion is able to publish

configured the salt-stack environment like below:
machine1 -> salt-master
machine2 -> salt-minion
machine3 -> salt-minion
This setup is working for me and I can publish i.e. the command "ls -l /tmp/" from machine2 to machine3 with
salt-call publish.publish 'machine3' cmd.run 'ls - /tmp/'
How it's possible to restrict the commands that are able to be published?
In the currently setup it's possible to execute every command on machine3 and that we would be very risky. I was looking in the salt-stack documentation but unfortunately, I didn't find any example how to configure it accordingly.
SOLUTION:
on machine1 create file /srv/salt/_modules/testModule.py
insert some code like:
#!/usr/bin/python
import subprocess
def test():
return __salt__['cmd.run']('ls -l /tmp/')
if __name__ == "__main__":
test()
to distribute the new module to the minions run:
salt '*' saltutil.sync_modules
on machine2 run:
salt-call publish.publish 'machine3' testModule.test
The peer configuration in the salt master config can limit what commands certain minion can publish, e.g.
peer:
machine2:
machine1:
- test.*
- cmd.run
machine3:
- test.*
- disk.usage
- network.interfaces
This will allow minion machine2 to publish test.* and cmd.run commands.
P.S. Allowing minions to publish cmd.run command is not a good idea generally, just put it here as example.

Saltstack set variables after a state

I have a question about SaltStack variables.
I want to set a folder name, something like:
{% set exim4_folder = salt['cmd.run']('ls /tmp | grep exim4') %}
but the folder I am trying to get is not available till the state I ran before that assignment:
download_source_code:
cmd.run:
- cwd: /tmp
- names:
- apt-get -y source exim4
- apt-get -y build-dep exim4
Is there a way to tell salt to run that assignment after I run "download_source_code"?
The problem you're going to run into here is that all the jinja sections of your sls file will be evaluated before any of the yaml Salt states are evaluated.
So your 'ls /tmp | grep exim4' will always be executed before your download_source_code state is executed.

How can I execute multiple commands using Salt Stack?

I tried to add:
mypack:
pkg:
- installed
- pkgs:
- mercurial
- git
cmd.run:
- name: 'mkdir -p /opt/mypack'
cmd.run: 'hg pull -u -R /opt/mypack || hg clone -R /opt https://...'
cmd.run: 'ln -s /opt/mypack/etc/init.d/xxx /etc/init.d/xxx'
But for some reason this the state seems to execute/install but the commands are not executed, or at least not all of them.
I need a solution to run multiple commands and to fail the deployment if any of these fails.
I know that I could write a bash script and include this bash script, but I was looking for a solution that would work with only the YAML file.
You want this:
cmd-test:
cmd.run:
- name: |
mkdir /tmp/foo
chown dan /tmp/foo
chgrp www-data /tmp/foo
chmod 2751 /tmp/foo
touch /tmp/foo/bar
Or this, which I would prefer, where the script is downloaded from the master:
cmd-test:
cmd.script:
- source: salt://foo/bar.sh
- cwd: /where/to/run
- user: fred
In addition to the above (better) suggestions, you can do this:
cmd-test:
cmd.run:
- names:
- mkdir -p /opt/mypack
- hg pull -u -R /opt/mypack || hg clone -R /opt https://...
- ln -s /opt/mypack/etc/init.d/xxx /etc/init.d/xxx
For reasons I don't understand yet (I'm a Salt novice), the names are iterated in reverse order, so the commands are executed backwards.
You can do as Dan pointed out, using the pipe or a cmd.script state. But it should be noted that you have some syntax problems in your original post. Each new state needs a name arg, you can't just put the command after the colon:
mypack:
pkg:
- installed
- pkgs:
- mercurial
- git
cmd.run:
- name: 'my first command'
cmd.run:
- name: 'my second command'
However, that actually may fail as well, because I don't think you can put multiple of the same state underneath a single ID. So you may have to split them out like this:
first:
cmd.run:
- name: 'my first command'
second:
cmd.run:
- name: 'my second command'
As one of the users pointed out above, this works in proper order (salt 3000.2)
install_borg:
cmd.run:
- names:
- cd /tmp
- wget https://github.com/borgbackup/borg/releases/download/1.1.15/borg-linux64
- mv borg-linux64 /usr/local/bin/borg
- chmod u+x /usr/local/bin/borg
- chown root:root /usr/local/bin/borg
- ln -s /usr/local/bin/borg /usr/bin/borg
- unless: test -f /usr/bin/borg

Resources