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.
Related
my web server generates a shell script with more than 100 lines of code based on complex user selections. I need to orchestrate this over several machines using salt-ssh. what I need is to copy this shell script to remote and execute it from there for all devices. how to achieve this with salt-ssh ?. I can not install minions on the remote device.
Just as with normal minion. Write a state...
add script:
file.managed:
- name: file.sh
- source: /path/to/file.sh
run script:
cmd.run:
- name: file.sh
...and apply it
salt-ssh 'minions*' state.apply mystate.sls
For example, I have a git.latest state like this:
foobar:
git.latest:
- branch: production
...
I want to define a cmd.run or cmd.wait state that runs if and only if one particular file in the foobar git repository has changed. I don't care if other files of the repo have changed.
How can I do that?
There doesn't seem to be anything immediately obvious in the current release of SaltStack (2015.8.4)
If you're using Github (or similar), you could use file.managed to watch the file for changes:
/tmp/egg:
file.managed:
- source: https://raw.githubusercontent.com/mafrosis/dotfiles/master/README.md
- source_hash: sha1=bf5b231d1b3dc9bc3217896f9f5d1c903b0779dd
cmd.wait:
- name: touch /tmp/bacon
- watch:
- file: /tmp/egg
You could dump the files git is going to change into a 'changed_files' file, then check for the file you want to check:
update_git:
cmd.run:
- name: git fetch && git --no-pager diff --no-color --name-only HEAD origin/{{ branch }} > /tmp/changed_files
- cwd: {{ directory }}
git.latest:
- name: {{ git_url }}
- branch: {{ git_branch }}
- target: {{ directory }}
update_npm_packages:
cmd.run:
- name: npm ci --only=production --unsafe-perm
- cwd: {{ directory }}
- onlyif: test `/tmp/changed_files | grep package-lock.json | wc -l` != 0
- require:
- cmd: update_git
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