-
-
Notifications
You must be signed in to change notification settings - Fork 355
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
documentation: passing dynamic role variables to tests when runnning molecule verify #345
Comments
Woops, meant to open this on https://github.com/metacloud/molecule, closing! :) |
Following ansible/molecule#1396 (comment), I'm not getting anywhere on the molecule repository. Could you provide any guidance here or could we suggest some sort of an API for this? I'd be happy to implement something. A simple use case to focus the discussion:
|
Testinfra should load host and group variables automatically. What do you mean by "I pass a variable to my role ?", is this a role "default" variable ? |
When I do this:
So, here, I meant that I pass a value to foobar. I want to be able to access foobar in my test.
I haven't been able to see any value for foobar when I use I've seen you mention that testinfra can't know about my playbooks but I assumed that it could pick up variables that are passed into the role. Hope that makes sense. |
I had a similar problem, because I wanted to access the role defaults and vars. I have the following in my conftest.py. I did it this way to keep the variable precedence of ansible intact. I used the cache because I had the problem that the host fixture was teared down and setup for every test which made this really slow.
Seems like we had pretty much the same idea :) |
Thanks for weighing in @barnabasJ! I am sure (from counting tickets on molecule and testinfra repositories) that this is a use case that many people need solved. I must say, your solution does look quite nice! Perhaps this could be solved as a family of test fixtures that come packaged up. Any thoughts @philpep? |
@barnabasJ : how do you use testinfra? It seems you don't use molecule. In my case, putting a conftest.py file in my test dir doesn't affect my tests. Moreover, I don't understand how you use your functions ansible_vars, more specifically I don't understand what is the request parameter. Could you be more explicit please? |
In my current environment it's hard to use docker or vms on my local machine, therefore I don't use molecule. But I write the tests to check if the servers are in the desired state after running ansible-playbooks. I just do it without molecule as a dev tool. |
OK thanks for the explaination.
Of course it could be very useful if this was integrated natively in |
i'd definitely love to see something like this officially included, having only used molecule and testinfra for about a week it was one of my biggest gripes. @dangoncalves that fixtures works great, thanks! |
There probably isn't one solution for everything. But if we had a documented example, I think most would be able to adapt it to their use-case. |
Question: are there any downsides to this approach of using Otherwise, can we come to some agreement on adding variable fixtures to the https://github.com/philpep/testinfra/blob/master/testinfra/plugin.py? It appears since these paths are totally configurable, we'll need defaults (like |
I'm still not sure I fully grasp the notion of why you need to pass variables around like that and why you need them available for your integration tests? I've read this issue multiple times and I cannot come up with a good reason to do this without it being overly complex and confusing for the person maintaining such a library. I think I would need to see an example repository with these additions included to see why it might be worth while effort to make a recommendation. I know the Ansible community does not believe unit testing but I feel that this scenario, testing variable combinations in custom roles to be more a unit test not an integration test. Testinfra is a integration test library and I could see adding such a new fixture would cause the test library to behave "weirdly" or "falsey" at times if the user has mis-configured their environment or has files laying around. If I understand your problem scenario then one of the ways I've solved something like this is to just create a new molecule Anyway, I really like this discussion and curious to learn more about how other folks are doing things. |
I have laid out a clear example in #345 (comment):
You're right, we need to avoid this. The default should always work.
Looking at https://github.com/codylane/ansible-role-pyenv/blob/master/molecule/debian/playbook.yml, you have specified This example is proving (IMHO) why we need to be able to access role variables in the tests. If you could access the pyenv versions values you passed to the role in the test, then you could simply iterate over them and make sure the directories are present. This would mean that your tests stay synced with your role variables. |
In my case we would also like to be able to run the test periodically on the servers after provisioning them. But not all the servers are exactly the same. This means hard coding the tests is not always possible. If I use the default_vars during development of the ansible roles. I can just specify the vars in the group_vars/host_vars later the way I would do it if I ran a playbook with multiple roles on multiple servers and the defaults are overwritten and the tests are specific to the servers they're run on. |
Ahh, I see both your points now. Thank you for taking the time to help clarify. lwm - I will fix that missing test, thank you for pointing it out. It was there, then I did a refactor due to travis "timeout" problems and I must have forgot to add it back in. I've really enjoyed this discussion. I'd like to see if I can take a stab at what I think is being proposed here. RequirementWhen using testinfra to test our ansible roles, we would like to have a way to pass in a group of variables that work for the following cases:
I could also see a need where the scope needs to also be a requirement to avoid the "setup" and "teardown" routines. Following the testinfra paradigm there are "module" and "function" scope test fixtures so we would probably want a way to do both for maximum flexibility. Question
Potential Solution DiscussionI wonder if we could explore the following scenarios? 1.) See if we can get ansible_runner to use a dynamic inventory where the dynamic inventory is a JSON blob of configuration. (I'll provide an example of this when I have more time) 2.) I also wonder if it is possible to use 3.) I wonder if we create a pytest fixture to push custom facts to the SUT (system under test) which would be available to both ansible and testinfra? I can also provide an example of this if anyone is interested. |
Just a quick comment on group_vars/host_vars. Yes the ansible_runner picks up on them but when creating roles I don't use those, I see them as a higher level tool used with playbooks. What do you think? |
Thanks! I wonder, as another idea, if we can't get this under https://testinfra.readthedocs.io/en/latest/modules.html#testinfra.modules.ansible.Ansible.get_variables since that is the existing API. That leads into https://github.com/philpep/testinfra/blob/master/testinfra/utils/ansible_runner.py which does use the official Ansible module - perhaps there is something we can do there. I will take a look when I get time. |
barnabasJ - I hear you and that makes a lot of sense from a testing perspective. My first thought then is to use a dynamic inventory script (i'm still working on an example) that is configured via a JSOB blob where each group would be a different test scenario of configuration data which is then passed to ansible. However, the challenge is trying to get at those variables through the 'ansible' library API and it's been a few years since I got intimate with it. It will take a few days as I have free time to sort it out but I've got a few more ideas to try. lwm - Yup, in my initial testing |
From initial testing, I think I understand why we cannot see the role variables it's because the ansible_runner does not see a playbook, instead it wraps what a playbook might look like via dictionary object So, with that said, we will need to introduce some new features for the
The result that should be returned is a new dictionary that represents However, before we introduce this new feature, I'd like to get consensus from the project owner @philpep if this solution would be acceptable to implement? @lwm and @barnabasJ - Please also chime in if I've missed anything. |
Great.
Should we use some naming convention to allow picking up and reading of vars files in the scenario folder?
Good idea. |
The runner is attached to the host which is The playbook dictionary object you linked to, is located in the run function which is executed everytime something is run on the target. Parsing the playbook and variables every time a command is invoked is very expensive time-wise. I'm also not sure if we even need such a complicated setup. Are you guys trying to read variables from the playbook itself too? Right now it's possible to specify the host from within a module using the
Maybe it'would be enough to add another option here, like specifying a role dir with the defaults and vars to parse. But like you said it's probably best to wait and see what @philpep thinks. |
I did play around with it a little bit. Here is a minimal example. All the needed stuff is already in the
I just copied most of this from the This is just thrown together and thought of as a proof of concept. |
@barnabasJ - Thanks for posting that little snippet that is exactly what I was thinking, however, I wonder in your example if are also experiencing only seeing the role default vars? For example, given
|
B U M P 🦈 🦈 🦈 |
Hi, I'm digging in this issue but I'm a bit confused because I'm not familiar with molecule and ansible. IIUC, you run ansible through molecule which run a playbook with "vars_files", "roles" and role variables, and you expect to have a way in testinfra tests to get theses variables (= merge of host_vars, group_vars, vars_file, role default vars and role overriden vars) ? testinfra doesn't have access to the playbook the machine was provisioned with, it only read inventory, ansible.cfg and host/groups vars. |
Thanks for taking some time on this! We kind of got lost at sea 😖
Yes, you got it!
I think, basically, we decided that it would be the nicest if we could extend the |
First of all, I'd like to thank you too for spending your time on this. I don't use molecule because I can't use docker on my work machine at the moment. During tests, I also like to use ansible variables though. But to test the roles in isolation, it's only possible for me to use the variables inside the role (defaults and vars). Therefore, I don't have a playbook for every role. I took a glance at the ansible code and saw that the smallest unit for getting the variables is a play. A playbook could potentially hold multiple Plays which might make it difficult to load the correct variables from every playbook. We would probably need to specify at least the playbook and optionally which play we want to load. For my specific use case, it would also be nice if I could just inline the necessary play data as a string or a dictionary and don't have to create a playbook for every role. Specifically, because I also run the tests of multiple roles combined to test a specific target host which had multiple plays run against it and the legacy roles I work with use the same variable name from time to time. Which would be problematic because some roles would overwrite values for other roles. |
Yes, I think role vars are scoped to the role.
It might be hard to write this, depending on the complexity of the ansible API, and by experience, it is so I think it's ok to not handle this for old ansible versions (and raise a NotImplementedError). |
Right, apologies @barnabasJ, I see we have differing needs.
<3 I've still not had time to do anything to make this happen but I promise I will QA test it :) |
Also looking forward this capability :) |
Not sure if a lot have changed in the code base since the discussion here have taken place but the This is the fixture I put in my molecule test file. Hope it can be useful to someone. from ansible.template import Templar
from ansible.parsing.dataloader import DataLoader
@pytest.fixture(scope='module')
def ansible_vars(host):
"""
Return a dict of the variable defined in the role tested or the inventory.
Ansible variable precedence is respected.
"""
defaults_files = "file=../../defaults/main.yml"
vars_files = "file=../../vars/main.yml"
host.ansible("setup")
host.ansible("include_vars", defaults_files)
host.ansible("include_vars", vars_files)
all_vars = host.ansible.get_variables()
all_vars["ansible_play_host_all"] = testinfra_hosts
templar = Templar(loader=DataLoader(), variables=all_vars)
return templar.template(all_vars, fail_on_undefined=False) |
Oh nice @sebastienGuavus! @philpep, do you have any indication on what kind of change you'd accept for this issue? I'd gladly put some time in on a pull request. Still would really like to see a more convenience API available for this in |
I've created a pull request to pass kwargs to ansible in order to write a python module which resolves and exposes role and testinfra variables as a tester fixture. I've outlined my idea with code examples in the pull request. |
The testaid pytest plugin should solve this problem. Originally, it was calling the ansible cli interface using the testinfra host fixture. But after the latest rewrite it is using the ansible python api directly and even exposes it in two fixtures. As testaid is independent from the host fixture it can now use the session scope which allows pytest to cache the testvars. |
One year later, unfortunately, as of almost mid-2020, the I think the solution should be found in the |
Indeed With yesterday's relase 0.4.0, The transition from |
I know this could be batted away as something that is related to testinfra only, however, I am finding that I would like to ask here - would you accept a documentation PR that outlines a strategy for including role variables that you passed to your converge playbook, into your unit tests?
Here's what I am currently doing:
Then in a
molecule/default/testvars.yml
, I have:Then, in my
molecule/default/tests/conftest.py
, I do the following:And in my tests, I can then:
As far as I can see, this is the cleanest way (I've digged around in a lot of tickets) of matching up the dynamic variables that you pass to your role and your testinfra pytest tests.
If it isn't, please someone tell me :)
In any case, the main question remains - is there a place we can start to document this on the RTD setup?
The text was updated successfully, but these errors were encountered: