{%- macro files_switch( source_files, lookup=None, default_files_switch=["id", "os_family"], indent_width=6, use_subpath=False ) %} {#- Returns a valid value for the "source" parameter of a "file.managed" state function. This makes easier the usage of the Template Override and Files Switch (TOFS) pattern. Params: * source_files: ordered list of files to look for * lookup: key under ":tofs:source_files" to prepend to the list of source files * default_files_switch: if there's no config (e.g. pillar) ":tofs:files_switch" this is the ordered list of grains to use as selector switch of the directories under "/files" * indent_width: indentation of the result value to conform to YAML * use_subpath: defaults to `False` but if set, lookup the source file recursively from the current state directory up to `tplroot` Example (based on a `tplroot` of `xxx`): If we have a state: Deploy configuration: file.managed: - name: /etc/yyy/zzz.conf - source: {{ files_switch( ["/etc/yyy/zzz.conf", "/etc/yyy/zzz.conf.jinja"], lookup="Deploy configuration", ) }} - template: jinja In a minion with id=theminion and os_family=RedHat, it's going to be rendered as: Deploy configuration: file.managed: - name: /etc/yyy/zzz.conf - source: - salt://xxx/files/theminion/etc/yyy/zzz.conf - salt://xxx/files/theminion/etc/yyy/zzz.conf.jinja - salt://xxx/files/RedHat/etc/yyy/zzz.conf - salt://xxx/files/RedHat/etc/yyy/zzz.conf.jinja - salt://xxx/files/default/etc/yyy/zzz.conf - salt://xxx/files/default/etc/yyy/zzz.conf.jinja - template: jinja #} {#- Get the `tplroot` from `tpldir` #} {%- set tplroot = tpldir.split("/")[0] %} {%- set path_prefix = salt["config.get"](tplroot ~ ":tofs:path_prefix", tplroot) %} {%- set files_dir = salt["config.get"](tplroot ~ ":tofs:dirs:files", "files") %} {%- set files_switch_list = salt["config.get"]( tplroot ~ ":tofs:files_switch", default_files_switch ) %} {#- Lookup source_files (v2), files (v1), or fallback to an empty list #} {%- set src_files = salt["config.get"]( tplroot ~ ":tofs:source_files:" ~ lookup, salt["config.get"](tplroot ~ ":tofs:files:" ~ lookup, []), ) %} {#- Append the default source_files #} {%- set src_files = src_files + source_files %} {#- Only add to [""] when supporting older TOFS implementations #} {%- set path_prefix_exts = [""] %} {%- if use_subpath and tplroot != tpldir %} {#- Walk directory tree to find {{ files_dir }} #} {%- set subpath_parts = tpldir.lstrip(tplroot).lstrip("/").split("/") %} {%- for path in subpath_parts %} {%- set subpath = subpath_parts[0 : loop.index] | join("/") %} {%- do path_prefix_exts.append("/" ~ subpath) %} {%- endfor %} {%- endif %} {%- for path_prefix_ext in path_prefix_exts | reverse %} {%- set path_prefix_inc_ext = path_prefix ~ path_prefix_ext %} {#- For older TOFS implementation, use `files_switch` from the config #} {#- Use the default, new method otherwise #} {%- set fsl = salt["config.get"]( tplroot ~ path_prefix_ext | replace("/", ":") ~ ":files_switch", files_switch_list, ) %} {#- Append an empty value to evaluate as `default` in the loop below #} {%- if "" not in fsl %} {%- set fsl = fsl + [""] %} {%- endif %} {%- for fs in fsl %} {%- for src_file in src_files %} {%- if fs %} {%- set fs_dirs = salt["config.get"](fs, fs) %} {%- else %} {%- set fs_dirs = salt["config.get"]( tplroot ~ ":tofs:dirs:default", "default" ) %} {%- endif %} {#- Force the `config.get` lookup result as a list where necessary #} {#- since we need to also handle grains that are lists #} {%- if fs_dirs is string %} {%- set fs_dirs = [fs_dirs] %} {%- endif %} {%- for fs_dir in fs_dirs %} {#- strip empty elements by using a select #} {%- set url = ( [ "- salt:/", path_prefix_inc_ext.strip("/"), files_dir.strip("/"), fs_dir.strip("/"), src_file.strip("/"), ] | select | join("/") ) %} {{ url | indent(indent_width, true) }} {%- endfor %} {%- endfor %} {%- endfor %} {%- endfor %} {%- endmacro %}