I've got a few configuration values in my application that are tied to ip, mac addr, server name, etc and am trying to come up with a modular state that will match on the correct value from my pillar with a pillar.get. I'm not finding any info on how to use grain values in pillar.get.
pillar example:
account:
qa:
server1:
appUsername: 'user2'
appPassword: 'password2'
server2:
appUsername: 'user2'
appPassword: 'password2'
prod:
server3:
appUsername: 'user3'
appPassword: 'password3'
server4:
appUsername: 'user4'
appPassword: 'password4'
Lines from my template:
keyUser={{ salt['pillar.get']('account:grains['env']:grains['id']:appUsername', 'default_user') }}
keyPass={{ salt['pillar.get']('account:grains['env']:grains['id']:appPassword', 'default_pass') }}
This just seems so natural, but whatever I try errors out, or will escape actual grain lookup and give me the defaults. I also can't find anything on google. Anybody have a solution? Should I dynamically set the appUsername and appPassword values on the pillar instead? I like the layout in the pillar as is, because it's a great easy to read lookup table, without a ton of conditional jinja.
First you can't just embed grains['env'] into the pillar lookup string- you'll need to concatenate. Second, your Jinja assignment looks wrong. Try this:
{% set keyUser = pillar.get('account:'~grains['env']~':'~grains['id']~':appUsername', 'default_user') %}
~ is the concatenate operator in Jinja.
Also, salt['pillar.get']('blah') is the same as pillar.get('blah').
However! It's difficult to be sure without the actual error and/or the full template.
Related
I am trying to work through the Salt Formulas documentation and seem to be having a fundamental misunderstanding of what a salt formula really is.
Understandably, this question may seem like a duplicate of these questions, but due to my failing to grasp the basic concepts I'm also struggling to make use of the answers to these questions.
I thought, that a salt formula is basically just a package that implements extra functions, a lot like
#include <string.h>
in C, or
import numpy as np
in Python. Thus, I thought, I could download the salt-formula-linux to /srv/formulas/salt-formula-linux/, add that to file_roots, restart the master (all as per the docs), and then write a file like swapoff.sls containing
disable_swap:
linux:
storage:
swap:
file:
enabled: False
(the above is somewhat similar to the examples in the repo's root) in hope that the formula would then handle removing the swap entry from /etc/fstab and running swapoff -a for me. Needless to say, this didn't work, clearly because I'm not understanding what a salt formula is meant to be.
So, what is a salt formula and how do I use it? Can I make use of it as a library of functions too?
This answer might not be fully correct in all technicalities, but this is what solved my problem.
A salt formula is not a library of functions. It is, rather, a collection of state files. While often a state file can be very simple, such as some of my user defined
--> top.sls <--
base:
'*':
- docker
--> docker.sls <--
install_docker_1703:
pkgrepo.managed:
# stuff
pkg.installed:
- name: docker-ce
creating a state file like
--> swapoff.sls <--
disable_swap:
linux.storage.swap: # and so on
is, perhaps, not the way to go. Well, at least, maybe not for a beginner with lacking knowledge.
Instead, add an item to top.sls:
- linux.storage.swap
This is not enough, however. Most formulas (or the state files within them, if you will) are highly parametrizable, i.e. they're full of placeholders with variable names, such as {{ swap.device }}. If there's nothing to fill this gap, the state fill will not be able to do anything. These gaps are filled from pillars.
All that remains, is to create a file like swap.sls in /srv/pillar/ that would contain something like (as per the examples of that formula)
linux:
storage:
enabled: true
swap:
file:
enabled: true
engine: file
device: /swapfile
size: 1024
and also /srv/pillar/top.sls with
base:
'*':
- swap
Perhaps /srv/pillar should also be included in pillar_roots in /etc/salt/master.
So now /srv/salt/top.sls runs /srv/formulas/salt-formula-linux/linux/storage/swap.sls which using the guidance of /srv/pillar/top.sls pulls some parameters from /srv/pillar/swap.sls and enables a swapfile.
Is there any way to cause rendering of a salt state to output a warning or error when a requested pillar is not found?
I'm setting up Saltstack for a system with a lot of components, and we use pillars to make sure different Salt states have the same values where appropriate. We also aim to keep all values that will differ from installation to installation in pillars. So there are many pillar files and the total amount of pillar variables is slightly unhinged. When we're installing this system again we'll have to make sure that all all pillars are defined and have an appropriate value. In this process it would greatly help to have warnings or errors when pillars are undefined, instead of having to grep for "None" through the entire minion, then working out where things went wrong. Is there any way to accomplish such warnings or errors?
We can't possibly be the only company with a complicated salt installation.
SOLUTION:
As linked in the approved answer, the solution is to add the following to /etc/salt/minion:
pillar_raise_on_missing: True
PILLAR_RAISE_ON_MISSING: True
Remember to restart salt-minion
You can make sure a pillar return an error if not defined like that:
{%- set port = pillar['db']['host'] %}
Or you can specify default value if there is none.
{%- set host = salt['pillar.get']('db:host', 'localhost') %}
If the pillar is defined but without value, you can use the pillar module, to get an error.
Check here: https://docs.saltstack.com/en/latest/ref/modules/all/salt.modules.pillar.html#salt.modules.pillar.get
Attempt to retrieve the named value from pillar, if the named value is
not available return the passed default. The default return is an
empty string except opts['pillar_raise_on_missing'] is set to
True, in which case a KeyError will be raised.
The default behavior is to produce an error if a requested pillar value isn't present. The documentation recommends using pillar.get with a default value so that missing values get filled in, but it's not required. To get hard errors from states, do an attribute lookup on the pillar instead.
Suppose you have a pillar file that looks like this:
some_app:
opt1: 23
opt2: null
Here are some methods of accessing them and what they will do:
{{ pillar['some_app']['opt1'] }} # Prints 23
{{ pillar.some_app.opt1 }} # Alternate syntax, also prints 23
{{ pillar['some_app']['missing_opt'] }} # ERROR
{{ pillar.some_app.missing_opt }} # ERROR
{{ pillar.get('some_app:missing_opt', 17) }} # No error, prints 17
{{ pillar['some_app']['opt2'] }} # Also no error. I think it prints None.
The methods marked as 'error' will make the state fail noisily when it is run, which I gather is what you want.
A specific case leading to a more general question: I have a salt formula that looks like this:
formula/
init.sls
some_other_state.sls
defaults.yaml
As part of both sls's behavior I want them to load defaults.yaml into a dict. I can do it this way in either of them:
{%- import_yaml formula/defaults.yaml as defaults %}
...but this hardcodes the location of the formula relative to the base of the salt tree, and will break if the tree is ever restructured and the formula moved to (say) a nested directory.
I tried this instead:
{%- import_yaml (slspath + "/defaults.yaml") as defaults %}
This works for init.sls but not for some_other_state.sls; the reason is that slspath expands to the containing directory for the former but the full path for the latter.
What I really want is some equivalent of slspath that always expands to "the directory containing the currently-running sls." I can then specify whatever path I need relative to that.
Does such a variable exist, and if so, what is it?
The variable you are looking for is tpldir which was added in 0c78d7dc.
I'm trying to populate a list with a dataset and set the selected option with a helper function that compares the current data with another object's data (the 2 objects are linked)
I made the same type of list population with static variables:
Jade-
select(name='status')
option(value='Newly Acquired' selected='{{isCurrentState "Newly Acquired"}}') Newly Acquired
option(value='Currently In Use' selected='{{isCurrentState "Currently In Use"}}') Currently In Use
option(value='Not In Use' selected='{{isCurrentState "Not In Use"}}') Not In Use
option(value='In Storage' selected='{{isCurrentState "In Storage"}}') In Storage
Coffeescript-
"isCurrentState" : (state) ->
return #status == state
This uses a helper isCurrentState to match a given parameter to the same object that my other code is linked to so I know that part works
The code I'm trying to get to work is :
Jade-
select.loca(name='location')
each locations
option(value='#{siteName}' selected='{{isCurrentLocation {{siteName}} }}') #{siteName}
Coffeescript-
"isCurrentLocation": (location) ->
return #locate == location
All the other parts are functioning 100%, but the selected part is not
I've also tried changing the way I entered the selected='' part in a manner of ways such as:
selected='{{isCurrentLocation "#{siteName}" }}'
selected='{{isCurrentLocation "#{siteName} }}'
selected='{{isCurrentLocation {{siteName}} }}'
selected='#{isCurrentLocation "{{siteName}}" }'
selected='#{isCurrentLocation {{siteName}} }'
selected='#{isCurrentLocation #{siteName} }'
Is what I'm trying to do even possible?
Is there a better way of achieving this?
Any help would be greatly appreciated
UPDATE:
Thanks #david-weldon for the quick reply, i've tried this out a bit and realised that I wasn't exactly clear in what I was trying to accomplish in my question.
I have a template "update_building" created with a parameter( a buidling object) with a number of attributes, one of which is "locate".
Locations is another object with a number of attributes as well, one of which is "siteName". One of the siteName == locate and thus i need to pass in the siteName from locations to match it to the current building's locate attribute
Though it doesn't work in the context I want to use it definitely pointed me in a direction I didn't think of. I am looking into moving the parent template(The building) date context as a parameter into the locations template and using it from within the locations template. This is easily fixable in normal HTML spacebars with:
{{>locations parentDataContext/variable}}
Something like that in jade would easily solve this
Short answer
selected='{{isCurrentLocation siteName}}'
Long answer
You don't really need to pass the current location because the helper should know it's own context. Here's a simple (tested) example:
jade
template(name='myTemplate')
select.location(name='location')
each locations
option(value=this selected=isCurrentLocation) #{this}
coffee
LOCATIONS = [
'Newly Acquired'
'Currently In Use'
'Not In Use'
'In Storage'
]
Template.myTemplate.helpers
locations: LOCATIONS
isCurrentLocation: ->
#toString() is Template.instance().location.get()
Template.myTemplate.onCreated ->
#location = new ReactiveVar LOCATIONS[1]
I looked into the datacontexts some more and ended up making the options that populate the select into a different template and giving that template a helper, accessing the template's parent's data context and using that to determine which location the building had saved in it so that I could set that option to selected
Jade-
template(name="location_building_option")
option(value='#{siteName}' selected='{{isSelected}}') #{siteName}
Coffeescript -
Template.location_building_option.helpers
'isSelected': ->
parent = Template.parentData(1)
buildSite = parent.locate
return #siteName == buildSite
Thanks #david-weldon, Your answer helped me immensely to head in the right direction
My understanding of the property syntax for org-babel/orgmode is that by using the header-args:R I should be able to set up "options" for all R src blocks in an org file. This will work if I declare them on the line in the BEGIN_SRC, but I don't want to type the same options over and over, and I would like to do it for the whole buffer. Using org-version 8.3(Beta)
Questions: I have misunderstood the syntax, or is there likely something wrong with my install?
#+TITLE: Test
#+DATE:
#+AUTHOR:
#+EMAIL: britt
#+LANGUAGE: en
#+SELECT_TAGS: export
#+EXCLUDE_TAGS: noexport
#+PROPERTY: header-args:R :session *R2*
* Introduction
Want to get buffer property to work
#+BEGIN_SRC R
library(ggplot2)
library(data.table)
#+END_SRC
Remove the column in front of session...
PS- You can find such info in my (incomplete, not up-to-date as I would like) Org Babel Refcard at https://github.com/fniessen/refcard-org-babel.