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.
Related
I have the following saltstack top file.
'blog.php.*':
- php-apps
- php-apps.blog
'app.php.*':
- php-apps
- php-apps.some-app
'*phpone*':
- php-apps
- php-apps.blog
- php-apps.some-app
When I run high state for the above to environments It works fine. like this
salt 'blog.php.*' state.highstate or salt 'app.php.*' state.highstate
But when I run the same for the third server it fails.
salt '*phpone*' state.highstate
Error:
No matching sls found for 'php_apps' in env 'base'
I went to the minion server and found that the init.sls file in php-apps is not being copied over to minion cache location /var/cache/salt/minion/files/base/php-apps
I am not able to find any logs of state file having any compilation error which could cause this.
I tried the following but It still does not work.
Cleared master cache
Cleared minion cache
Recreated minion from scratch
What am I missing? Please let me know if any other information is required.
First , I will use yaml validator to validate the yaml meta structure. i.e.. install kwalify
#install kwalify
sudo apt-get install kwalify
# Now try to check the top file with yaml meta-validation
kwalify -m top.sls
# to check many yaml sls file
find . | grep "sls" | xargs kwalify -m
Don't be surprised when salt doesn't verify the most basic meta structure.
Because saltstack using YAML, it also suffer from tab vs space indentation issues, if you didn't force your editor to convert all TABS to fix spaces.
I'm getting my feet wet with SaltStack. I've made my first state (a Vim installer with a static configuration) and I'm working on my second one.
Unfortunately, there isn't an Ubuntu package for the application I'd like my state to install. I will have to build the application myself. Is there a "best practice" for doing "configure-make-install" type installations with Salt? Or should I just use cmd?
In particular, if I was doing it by hand, I would do something along the lines of:
wget -c http://example.com/foo-3.4.3.tar.gz
tar xzf foo-3.4.3.tar.gz
cd foo-3.4.3
./configure --prefix=$PREFIX && make && make install
There are state modules to abstract the first two lines, if you wish.
file.managed: http://docs.saltstack.com/ref/states/all/salt.states.file.html
archive.extracted: http://docs.saltstack.com/ref/states/all/salt.states.archive.html
But you could also just run the commands on the target minion(s).
install-foo:
cmd.run:
- name: |
cd /tmp
wget -c http://example.com/foo-3.4.3.tar.gz
tar xzf foo-3.4.3.tar.gz
cd foo-3.4.3
./configure --prefix=/usr/local
make
make install
- cwd: /tmp
- shell: /bin/bash
- timeout: 300
- unless: test -x /usr/local/bin/foo
Just make sure to include an unless argument to make the script idempotent.
Alternatively, distribute a bash script to the minion and execute. See:
How can I execute multiple commands using Salt Stack?
As for best practice? I would recommend using fpm to create a .deb or .rpm package and install that. At the very least, copy that tarball to the salt master and don't rely on external resources to be there three years from now.
Let's assume foo-3.4.3.tar.gz is checked into GitHub. Here is an approach that you might pursue in your state file:
git:
pkg.installed
https://github.com/nomen/foo.git:
git.latest:
- rev: master
- target: /tmp/foo
- user: nomen
- require:
- pkg: git
foo_deployed:
cmd.run:
- cwd: /tmp/foo
- user: nomen
- name: |
./configure --prefix=/usr/local
make
make install
- require:
- git: https://github.com/nomen/foo.git
Your configuration prefix location could be passed as a salt pillar. If the build process is more complicated, you may consider writing a custom state.
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.
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
I need to run node on my Ubuntu machine with sudo access. The directory of node is in the sudo path but when trying to run it i get a command not found. I can explicitly call node which does work.
//works
node
>
which node
/root/local/node/bin/node
echo sudo $PATH
sudo /root/local/node/bin:/usr/bin/node:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
sudo node --version
sudo: node: command not found
//explicitly calling it works
sudo node /root/local/node/bin
>
Um, I don't think there's such a thing as a "sudo path" - your second command there is just echoing "sudo" followed by your regular path. In any case, if you're running things with sudo you really, really should not depend on a path - you should give the explicit pathname for every command and file argument whenever possible, to minimize security risks. If sudo doesn't want to run something, you need to use visudo to add it to /etc/sudoers.