Problem:
We are currently trying to transition most of our Foreman configuration to Ansible and are utilizing the theforeman.foreman Ansible collection for this.
What we currently try to achieve is:
We want to manage Puppet smart-class parameters through Ansible
Due to how our workflows are designed, we need to only manage certain overrides through Ansible
The values we do manage need to be created or updated as needed
The values we do not manage need to stay untouched
The last part is the one we struggle with. From what we found, overrides for smart-class parameters seem to be āAll or Nothingā. If we do not specify any overrides, all overrides stay as they are. As soon as we start specifying overrides, all non-managed overrides get deleted.
Expected outcome:
A way to only manage a certain subset of overrides, either via a parameter to the smart_class_parameter module or via a own dedicated module.
Foreman and Proxy versions:
3.2.1 (I know this is dated, but should not be related)
Foreman and Proxy plugin versions:
foreman-tasks 6.0.1
foreman_expire_hosts 7.0.4
foreman_hooks 0.3.17
foreman_puppet 3.0.7
foreman_remote_execution 6.0.0
foreman_scc_manager 1.8.20
foreman_snapshot_management 2.0.1
foreman_templates 9.1.0
foreman_webhooks 3.0.3
katello 4.4.2.2
puppetdb_foreman 5.0.0
Distribution and version:
RHEL 8
Other relevant data:
Is suspect this loop to be responsible for what we see.
If I overlooked some way this is already doable, please share with me how that can be achieved
If this is not doable right now: Is this intended behavior or just an unconsidered use case? What would be an acceptable way of implementing it? My first idea would be to just add on option making the above mentioned loop skipable if desired.
yes, the current behaviour is intentional (even if not optimal in all cases, as you point out).
Without the āremove not listed overridesā there would be no way to remove overrides that you previously created and now want gone (via ansible).
The original design idea is/was that you have a playbook that defines all the needed overrides (= green field) and if you want one gone you just drop it from the list and magic happens.
The correct solution for brown field setups (= where you want/can only manage certain overrides) would be to have an own smart_class_parameter_override module, where you can manage individual overrides instead of all of one parameter in the parameter module. But we donāt have such a module today.
First off, thanks for the clarification I that understood the current situation correctly.
Concerning the problem we are currently facing: I understand that current behavior is an interesting scenario for many use cases. In my opinion, itās a rather bad situation to have it as the only option though.
I would assume most scenarios where people start to adopt Ansible management of their Foreman instance are brown field scenarios, plus the current behavior is most practical if you do not use FQDN based overrides a lot. By enforcing this behavior, you also forcefully remove the usersā ability to integrate Foreman with other tools on this end.
I do understand the need to remove overrides via Ansible, but that should be implementable rather easily. The backend code already can delete overrides (otherwise this problem would not be a topic at all), itās just a question of how to expose it to the user. My initial thought was to just expose possibilities through the ForemanSmartClassParameterModule to choose between ādelete unmanagedā true/false, and additionaly (after reading your thoughts) also exposing the desired state of the overrides.
I agree that having a dedicated smart_class_parameter_override module would probably be the better way, though I would at least feel semi-confident in my skills to implement the first option, while not really having that confidence for implementing a whole new module. (Once I manage to at least get the tests running on may machine to begin with, but thatās a different topic).
Would you be against exposing those options through ForemanSmartClassParameterModule in general or would this be an option worth looking into?
Alright, thatās a solution I#m even more happy with
Thanks for your effort, if you need someone to test the new module out let me know, otherwise Iām just looking forward to the release
From the looks of it, this looks good
I was at first a little confused about state being missing from both the parameter list and the documentation (it is present in smart_class_parameters, for example), but since you are using it in the tests and other modules are also lacking the explicit mention of state, I assume this is a cosmetic thing and the existance of state is always implied.
I am currently trying to spin up some tests and will get back to you once I can tell if this implementation solves our use-cases
It took me a while to set up some tests (Iām still pretty new to Ansible), but after running a few tests against our dev environment, it looks good overall. The only issue I found so far is some sort of type confusion, though I donāt know if this is related to the module or a general Ansible problem. Also, I only found this due to some faulty data in our Foreman instance in the first place (we are currently transitioning a lot of the life data to ansible to enable reproducible Foreman setups).
We have an sc-param that is supposed to take a package name string as argument. Someone now entered a string that looks like a list into this field, like ["package-name"]. It is still a string though.
The data for the Ansible Task to set this value looks like this:
So from the looks of it, somewhere on the way the string that contains the syntax for a list got converted to a list and the module would try to write that to Foreman (hopefully failing, since the parameter is declared to be a string).
While in my case this is obviously due to faulty data, I guess there might be use-cases where people need this? Not a blocker for me though, just wanted to bring it up.
Thanks for the fix, I can confirm the new version does not produce type-confusions anymore
I am still getting a change because now the nested double quotes get converted to single quotes, though I expect that to be a lesser problem.
Data from the data file we pass to ansible: '["package-name"]'
So in case one has a sc-param defined as string where the other end expects a json list, this might in fact cause problems. Probably an edge-case, since it requires you have set up your data type wrong in the first place, but maybe worth fixing.
I am not sure how much āguessingā I want to do in the conversion. Especially, if it already is a string, encoding is as json is a bad idea (as that would add quotes).
Iāll think a bit about this, but as you already say, this is quite a corner case (and we can fix it later, if we deem that necessary)
I can confirm your changed fixed what happened on our end. No more unintended changes on my test playbook.
The problem with the failed CI job seems to be that the JSON values in the test data are also strings and because of that, are picked up by and not isinstance(value, six.string_types) which causes the code to handle them as strings and not as json.
Though, I canāt figure out why it broke in the first place and stopped breaking now. Both ways should lead to the exact same block of code for strings
parameter_string = value
Also in my tests (with pure python though, not any ansible magic involved), calling str('["something"]'') actually results in the expected '["something"]', so my confusion is actually even greater in regards to why this bit of code keeps doing weird conversions.
The only way I found to actually break it was by calling json.dumps() on the string.