How to I tell salt to install one package or another? - salt-stack

In order to successfully install something I need the add-apt-repository command, which happens to be inside python-software-properties on some distros or on common-software-properties.
That is the easiest way to write .sls file that will do this and install one of these packages.

Use jinja conditionals and minion grains.
The following assumes I got the pairing correct for RedHat
add-apt-repository:
pkgs.installed:
- pkgs:
{% if grains['os_family'] == 'RedHat' %}
- common-software-properties
{% elif grains['os_family'] == 'Debian' %}
- python-software-properties
{% endif %}

Related

ansible mingw32-make out of memory

I try to automate a build via ansible on window7 Virtual Machine.
My build is base on cmake (version 3.7.1) and mingw32 (version 4.9.2) as compiler.
If I do the build directly in the VM by enter manually all command in the powershell, everything work find.
git clone --recursive somedepot
cd somedepot
cmake.bat .
mingw32-make all
Note : cmake.bat is the following script :
#cmake.exe -G"MinGW Makefile" %*
But when I try to do the same by ansible I got "cc1plus.exe: out of memory allocating 176080 bytes\r\nmingw32-make[2]" at the execution of mingw32-make all. But not on all build failed, I test my script before in a simple build and work well. It's when I wanted to go to the "real build" (which is more bigger) that the problem append.
Here my playbook :
- name: Some Build
hosts: win_build
tasks :
- name: Get src
win_command: git clone --recursive --branch "{{ tag_src }}" "{{ url_src }}" "{{ path_cmake }}"
- name: CMake
win_command: cmake.bat .
args:
chdir: "{{ path_cmake }}"
- name: Make
win_command: mingw32-make all
args:
chdir: "{{ path_cmake }}"
Thanks in advance.
I found the problem.
It was a bug in powershell3. I applied microsoft hotfix and everything work fine.

Using SaltStack, how can I watch for *one* file change in a git repository?

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

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 do you use the 'publish' module in your own module

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.

How do you prevent a dpkg installation task to notify a changed state when it runs for the second time?

There isn't a module for installing .deb packages directly. When you have to run dpkg as a command, it always mark the installation task as one that has changed. I'd some trouble configuring it correctly, so I'm posting here as a public notebook.
Here is the task to install with dpkg:
- name: Install old python
command: dpkg -i {{ temp_dir }}/{{ item }}
with_items:
- python2.4-minimal_2.4.6-6+precise1_i386.deb
- python2.4_2.4.6-6+{{ ubuntu_release }}1_i386.deb
- libpython2.4_2.4.6-6+{{ ubuntu_release }}1_i386.deb
- python2.4-dev_2.4.6-6+{{ ubuntu_release }}1_i386.deb
The files where uploaded to {{temp_dir}} in another task.
The answer below still works, but newer ansible versions have the apt module. Mariusz Sawicki answer is the preferred one now. I've marked it as the accepted answer.
It'll work just with Ansible version 1.3, when changed_when parameter was added. It is a little clumsy, maybe someone can improve the solution. I didn't find the documentation of this "register" object.
- name: Install old python
command: dpkg --skip-same-version -i {{ temp_dir }}/{{ item }}
register: dpkg_result
changed_when: "dpkg_result.stdout.startswith('Selecting')"
with_items:
- python2.4-minimal_2.4.6-6+precise1_i386.deb
- python2.4_2.4.6-6+{{ ubuntu_release }}1_i386.deb
- libpython2.4_2.4.6-6+{{ ubuntu_release }}1_i386.deb
- python2.4-dev_2.4.6-6+{{ ubuntu_release }}1_i386.deb
Here you can run the same task and it will just install the first time. After the first time, the packages won't be installed.
There were two modifications. One is the parameter --skip-same-version for preventing dpkg to reinstall the software. The other is the register and changed_when attributes. The first time dpkg runs, it prints to stdout a string starting with 'Selecting' and a change is notified. Later it will have a different output. I've tried a more readable condition, but couldn't make it work with a more sofisticated condition that uses "not" or searches for a substring.
In Ansible 1.6 (and newer), the apt module has a deb option:
- apt: deb=/tmp/mypackage.deb
You could use apt module with dpkg_options parameter:
- name: Install old python
apt: deb={{ temp_dir }}/{{ item }} dpkg_options="skip-same-version"
register: dpkg_result
changed_when: dpkg_result.stderr.find("already installed") == -1
with_items:
- python2.4-minimal_2.4.6-6+precise1_i386.deb
- python2.4_2.4.6-6+{{ ubuntu_release }}1_i386.deb
- libpython2.4_2.4.6-6+{{ ubuntu_release }}1_i386.deb
- python2.4-dev_2.4.6-6+{{ ubuntu_release }}1_i386.deb

Resources