Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix: Set loader on local copy of jinja environment #5

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

indy-independence
Copy link
Contributor

When adding the feature to specify a user defined jinja_env I think I opened up an opportunity for users to shoot themselves in the foot (I certainly did myself) by setting the env.loader in the referenced jinja_env argument instead of a local copy of the jinja_env object. If the code calling template_file uses the same jinja_env object in two threads with different FileSystemLoader paths, for example when one thread renders EOS templates from one directory and another thread renders JunOS templates from another directory, they will both update the same env.loader path causing the right templates not to be found. This could be fixed by the code calling template_file of course, but I think it's probably safer to fix with something like this patch maybe?

@dbarrosop
Copy link
Contributor

Would you mind adding an example of code that reproduces the issue you describe?

@indy-independence
Copy link
Contributor Author

Yes I have something like this:

cnaas_jinja_env = JinjaEnvironment(
    trim_blocks=True,
    lstrip_blocks=True,
    keep_trailing_newline=True
)

def blabla():
    r = task.run(task=template_file,
                 name="Generate device config",
                 template=template,
                 jinja_env=cnaas_jinja_env,
                 path=f"{local_repo_path}/{task.host.platform}",
                 **template_vars)

If the inventory that you run the task on contains both devices with task.host.platform eos and junos for example you will get errors with templates not found or including wrong templates etc.

@dbarrosop
Copy link
Contributor

dbarrosop commented May 25, 2021

Mmm, I think the main issue here is supporting both passing the env and path at the same time. I think that if we send the environment we shouldn't make any modification to it and simply pass it around. So the way I see it we should change the plugin to:

    if jinja_env:
        env = jinja_env
    else:
        env = Environment(
            loader=FileSystemLoader(path), undefined=StrictUndefined, trim_blocks=True,
        )

And then, in your code you'd have something like:

def per_vendor_env(path):
   return JinjaEnvironment(
       trim_blocks=True,
       lstrip_blocks=True,
       keep_trailing_newline=True,
       loader=FileSystemLoader(path),
   )

def blabla():
    r = task.run(task=template_file,
                 name="Generate device config",
                 template=template,
                 jinja_env= per_vendor_env(f"{local_repo_path}/{task.host.platform}"),
                 **template_vars)

Thoughts?

P.S: If you cared about efficiency the function could even use a cache to create a single env per path

@indy-independence
Copy link
Contributor Author

Yes that also makes sense to me. It might break some stuff on my end when I upgrade nornir, but as long as I patch my code before upgrading I think it should be fine :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants