复用Salt State代码片段:寻求非Python实现的自定义状态方案
Hey there! I totally get wanting to avoid writing custom Python modules when you’ve got repetitive repo and GPG key configs in your Salt States. Let’s dive into some solid non-Python approaches to abstract that code:
1. Use Jinja Macros (Most Straightforward)
Jinja macros are perfect for wrapping reusable chunks of Salt State code. You define a macro once, then call it with different parameters wherever you need it.
First, create a macro file (e.g., salt://_macros/repo_setup.sls) with your reusable repo/GPG logic:
{% macro setup_repo(repo_name, repo_url, gpg_key_url) %} {{ repo_name }}_repository: pkgrepo.managed: - humanname: {{ repo_name }} Official Repository - name: deb {{ repo_url }} {{ grains['oscodename'] }} main - file: /etc/apt/sources.list.d/{{ repo_name }}.list - gpgcheck: 1 - key_url: {{ gpg_key_url }} {{ repo_name }}_gpg_key: file.managed: - name: /usr/share/keyrings/{{ repo_name }}-archive-keyring.gpg - source: {{ gpg_key_url }} - mode: '0644' {% endmacro %}
Then, in any State file where you need to set up a repo, import the macro and call it with your specific values:
{% from '_macros/repo_setup.sls' import setup_repo %} # Set up Nginx repo {{ setup_repo('nginx', 'https://nginx.org/packages/mainline/debian', 'https://nginx.org/keys/nginx_signing.key') }} # Set up Docker repo {{ setup_repo('docker', 'https://download.docker.com/linux/debian', 'https://download.docker.com/linux/debian/gpg') }}
2. Pillar-Driven Configuration with Jinja Loops
If you want to manage all your repo configs in a centralized place, store them in Pillar and use a Jinja loop to render the States dynamically.
First, add your repo configs to your Pillar data (e.g., pillar/repos.sls):
repo_configs: nginx: url: https://nginx.org/packages/mainline/debian gpg_key_url: https://nginx.org/keys/nginx_signing.key docker: url: https://download.docker.com/linux/debian gpg_key_url: https://download.docker.com/linux/debian/gpg nodejs: url: https://deb.nodesource.com/node_20.x gpg_key_url: https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key
Then, create a State file that loops through the Pillar data to generate the repo/GPG states:
{% for repo_name, config in pillar.get('repo_configs', {}).items() %} {{ repo_name }}_repository: pkgrepo.managed: - humanname: {{ repo_name }} Repository - name: deb {{ config.url }} {{ grains['oscodename'] }} main - file: /etc/apt/sources.list.d/{{ repo_name }}.list - gpgcheck: 1 - key_url: {{ config.gpg_key_url }} {{ repo_name }}_gpg_key: file.managed: - name: /usr/share/keyrings/{{ repo_name }}-archive-keyring.gpg - source: {{ config.gpg_key_url }} - mode: '0644' {% endfor %}
Now, adding a new repo just requires updating your Pillar data—no need to touch the State file!
3. Parameterized SLS Files with Includes
You can also create a generic State file that pulls values from Pillar, then include it multiple times with different Pillar parameters for each repo.
Create a generic repo State file (e.g., salt://repo/init.sls):
{{ pillar.get('repo_name') }}_repository: pkgrepo.managed: - humanname: {{ pillar.get('repo_name') }} Repository - name: deb {{ pillar.get('repo_url') }} {{ grains['oscodename'] }} main - file: /etc/apt/sources.list.d/{{ pillar.get('repo_name') }}.list - gpgcheck: 1 - key_url: {{ pillar.get('gpg_key_url') }} {{ pillar.get('repo_name') }}_gpg_key: file.managed: - name: /usr/share/keyrings/{{ pillar.get('repo_name') }}-archive-keyring.gpg - source: {{ pillar.get('gpg_key_url') }} - mode: '0644'
Then, in your main State file, include it multiple times with different Pillar overrides:
# Set up Nginx repo include: - repo pillar: repo_name: nginx repo_url: https://nginx.org/packages/mainline/debian gpg_key_url: https://nginx.org/keys/nginx_signing.key # Set up Docker repo include: - repo pillar: repo_name: docker repo_url: https://download.docker.com/linux/debian gpg_key_url: https://download.docker.com/linux/debian/gpg
Final Notes
All these methods let you reuse your repo/GPG config logic without writing a single line of Python. Jinja macros are great for explicit, one-off reuse, while the Pillar-driven approach is ideal for centralized, scalable management.
内容的提问来源于stack exchange,提问作者Robert Munteanu

