Remove the empty lines left by Jinja2 variable definitions - jinja2

When writing template files using Jinja2 for Saltstack, I often define some variables at the beginning of the file. For example:
{% set ip = grains['ip4_interfaces']['eth1'][0] %}
{% set domain = pillar['company_domain'] %}
{% set version = pillar['site_version'] %}
{% set site_url = 'www.' + domain %}
[...]
Everything works fine but when opening the generated file, I get a block of empty lines where the jinja code was.
Am I doing something wrong ?
If not, is there any way to get rid of those empty lines when using templates ?

There is whitespace control in Jinja2. You might want:
{%- set ip = grains['ip4_interfaces']['eth1'][0] -%}
{%- set domain = pillar['company_domain'] -%}
{%- set version = pillar['site_version'] -%}
{%- set site_url = 'www.' + domain -%}
[...]
As well, the salt configuration file supports jinja_trim_blocks and jinja_lstrip_blocks (jinja_env:trim_blocks, jinja_env:lstrip_blocks, jinja_sls_env:trim_blocks, and jinja_sls_env:lstrip_blocks as of 2018.3).

Related

DBT - only insert\merge columns that exist in the source object

I'm new to dbt and jinja but trying my best.
We have a scenario where when using incremental merge our destination table schema is different from our Source schema so we'd like to only update/insert the common columns.
I'm loading my source columns into a variable then sending it as a configuration value like so:
{%- set src_cols = adapter.get_columns_in_relation(ref('pre_Dim_Entities_Client')) -%}
{{
config(
materialized='incremental',
unique_key='Entity_ID',
source_columns = src_cols
)
}}
SELECT *
FROM {{ ref ('pre_Dim_Entities_Client')}}
Then I've overridden merge.sql macro:
{% macro default__get_merge_sql(target, source, unique_key, dest_columns, predicates) -%}
{%- set predicates = [] if predicates is none else [] + predicates -%}
{%- set temp = config.get('source_columns') -%}
{{- print(temp) -}}
{%- set dest_cols_csv = get_quoted_csv(config.get('source_columns', default = dest_columns) | map(attribute="name"))%}
{%- set update_columns = config.get('merge_update_columns', default = dest_columns | map(attribute="quoted") | list) -%}
{%- set sql_header = config.get('sql_header', none) -%}
I've assigned the config value to a temp var and printed it to confirm what I suspected was the issue - the columns are not passed in source_columns configuration as I would have expected.
What am I doing wrong, and alternatively- Is there a better way to go about this issue?
Self-answer here -
Turns out passing variables in config block is problematic.
Instead I sent the source object name as a config value and in the merge macro retrieved the columns and updated dest_columns to the retrieved object.
{{
config(
materialized='incremental',
unique_key='Entity_ID',
source_scehma = 'pre_Dim_Entities_Client'
)
}}
SELECT *
FROM {{ ref ('pre_Dim_Entities_Client')}}
And in the merge macro:
{% macro default__get_merge_sql(target, source, unique_key, dest_columns, predicates) -%}
{%- set predicates = [] if predicates is none else [] + predicates -%}
{%- set src_name = config.get('source_scehma', none) -%}
{% if src_name %}
{%- set dest_columns = adapter.get_columns_in_relation(ref(src_name)) -%}
{% endif %}
{%- set dest_cols_csv = get_quoted_csv( dest_columns | map(attribute="name")) -%}
{%- set update_columns = config.get('merge_update_columns', default = dest_columns | map(attribute="quoted") | list) -%}
{%- set sql_header = config.get('sql_header', none) -%}
##Note:
In my solution I assume all source columns exist in the destination table.
If your need is to only take the common columns between source and destination the code should be modified accordingly.

Jekyll how can I use site.data.+variable?

I would like to know if there's a way to use a variable after a data call in Jekyll?
Like this:
{% assign variable = items %}
{% assign images = site.data.folder.variable %}
You can use a variable as an object key using this notation:
{% assign images = site.data.folder[variable] %}

Comparing timestamps gives type error in jinja2 template

I'm trying to compare two timestamps but getting a type error:
"TypeError: '<' not supported between instances of 'float' and 'str'
The template code is using 'as_timestamp' and the documentation recommended using float() when comparing.
I'm trying to get the name and date of the sensor that has a date value that is closest to now() (in Home Assistant).
So I'm trying to do the following:
{%- set list = ['sensor.1','sensor.2','sensor.3','sensor.4'] -%}
{%- set data = namespace(min_date=float(as_timestamp(now() + timedelta(days = 360))), min_name = '') -%}
{%- for i in list %}
{%- set value = states(i) %}
{%- if float(as_timestamp(value)) < float(data.min_date) %}
{%- set data.min_date = value -%}
{%- set data.min_name = state_attr(i, 'friendly_name') -%}
{% endif %}
{%- endfor %}
{{ data.min_name + ' (' + data.min_date | timestamp_custom('%Y-%m-%dT%H:%M:%S%z') + ')' }}
Any ideas why data.min_date is a str in the comparison, despite the value being defined with both float() and as_timestamp() ?
had to remove the 'as_timestamp' from the initial value assignment and add it to the if statement. that solved it.

How do I use a variable inside an include statement in a Jekyll file?

I have a multilingual Jekyll website and I'm trying to build a page that uses an include that will append a file based on the language used but I'd like to avoid using an if statement.
I tried to use the following options without success:
{%- assign filename = "myFile." -%}
{%- assign lang = site.active_lang -%}
{%- assign fileExtension = ".html" -%}
{%- assign file = filename | append: lang | append: fileExtension -%}
{%- include file -%}
I was expecting the include to include run like this:
{%- include myfile.en.html -%}
But the include is adding a string into the page instead of the desired file.
Does anyone know if this is possible or I'll have to give up and use an if statement that is proven to work?
Solved using {{}} around the variable, like this:
{%- include {{file}} -%}

how to access pillar data with variables?

I have a pillar data set like this;
vlan_tag_id:
nginx: 1
apache: 2
mp: 3
redis: 4
in the formula sls file I do this;
{% set tag = pillar.get('vlan_tag_id', 'u') %}
so now I have a variable tag which is a dictionary {'apache': 2, 'nginx': 1, 'redis': 4, 'mp': 3}
At run time I pass a pillar data app whose value will be either
1. apache
2. nginx
3. redis
4. mp
so if at run time I pass apache I want to something which will get me the value 2
I cant do {{ salt['pillar.get']('vlan_tag_id:app', '')}} because app itself is a variable.
I tried doing {{ salt'pillar.get'}}, but it throws error.
how can I do this ?
Since tag is just another dictionary, you can do a get on that as well:
{%- set tag = pillar.get('vlan_tag_id', 'u') %}
{%- set app = pillar.get('app') %}
{{ tag.get(app) }} # Note lack of quotes
If you want to use the colon syntax, you can append the contents of app to the key string:
{%- set app = pillar.get('app') %}
{{ salt['pillar.get']('vlan_tab_id:' + app) }}
I find it simpler to follow if I alias pillar.get and break it up a bit:
{%- set pget = salt['pillar.get'] %}
{%- set app = pget('app') %}
{%- set tag = pget('vlan_tag_id') %}
{{ tag.get(app) }}