Plugins legacy_facts and top_scope_facts interfer with each other on {::fqdn} inside strings
ConradGroth opened this issue · comments
Describe the Bug
The two plugins legacy_facts and top_scope_facts together produce invalid content inside strings.
Expected Behavior
Correct puppet syntax after autocorrection by plugins.
Steps to Reproduce
We had following puppet code (hence the usage of legacy fact ::fqdn at three places):
@@nagios_service { "check_ntp_${::fqdn}":
ensure => present,
check_command => 'check_nrpe!check_ntp',
host_name => $::fqdn,
target => "${nagios_cfg}/check_ntp_${::fqdn}.cfg",
}
A pdk validate -a --puppet-version=6
converts it to:
@@nagios_service { "check_ntp_${facts['facts['networking']['fqdn']']}":
ensure => present,
check_command => 'check_nrpe!check_ntp',
host_name => $facts['networking']['fqdn'],
target => "${nagios_cfg}/check_ntp_${facts['facts['networking']['fqdn']']}.cfg",
}
This produces invalid puppet code because of the duplicate facts[' text inside the two Strings. The $::fqdn variable inside the host_name attribute gets replaces correctly, although it is also found by both plugins.
The debug output shows that both plugins find exactly the same token to replace:
{
"message": "legacy fact",
"line": 16,
"column": 35,
"token": "#<PuppetLint::Lexer::Token:0x0000000003013a30>",
"fact_name": "fqdn",
"kind": "fixed",
"check": "legacy_facts",
"fullpath": "/root/my_module/manifests/icinga/ntp.pp",
"path": "manifests/icinga/ntp.pp",
"filename": "ntp.pp",
"KIND": "FIXED"
},
{
"message": "legacy fact",
"line": 21,
"column": 28,
"token": "#<PuppetLint::Lexer::Token:0x000000000301eea8>",
"fact_name": "fqdn",
"kind": "fixed",
"check": "legacy_facts",
"fullpath": "/root/my_module/manifests/icinga/ntp.pp",
"path": "manifests/icinga/ntp.pp",
"filename": "ntp.pp",
"KIND": "FIXED"
},
{
"message": "legacy fact",
"line": 23,
"column": 55,
"token": "#<PuppetLint::Lexer::Token:0x0000000003021e78>",
"fact_name": "fqdn",
"kind": "fixed",
"check": "legacy_facts",
"fullpath": "/root/my_module/manifests/icinga/ntp.pp",
"path": "manifests/icinga/ntp.pp",
"filename": "ntp.pp",
"KIND": "FIXED"
},
{
"message": "top scope fact instead of facts hash",
"line": 16,
"column": 35,
"token": "#<PuppetLint::Lexer::Token:0x0000000003013a30>",
"kind": "fixed",
"check": "top_scope_facts",
"fullpath": "/root/my_module/manifests/icinga/ntp.pp",
"path": "manifests/icinga/ntp.pp",
"filename": "ntp.pp",
"KIND": "FIXED"
},
{
"message": "top scope fact instead of facts hash",
"line": 21,
"column": 28,
"token": "#<PuppetLint::Lexer::Token:0x000000000301eea8>",
"kind": "warning",
"check": "top_scope_facts",
"fullpath": "/root/my_module/manifests/icinga/ntp.pp",
"path": "manifests/icinga/ntp.pp",
"filename": "ntp.pp",
"KIND": "WARNING"
},
{
"message": "top scope fact instead of facts hash",
"line": 23,
"column": 55,
"token": "#<PuppetLint::Lexer::Token:0x0000000003021e78>",
"kind": "fixed",
"check": "top_scope_facts",
"fullpath": "/root/my_module/manifests/icinga/ntp.pp",
"path": "manifests/icinga/ntp.pp",
"filename": "ntp.pp",
"KIND": "FIXED"
}
Environment
- PDK Version 2.4.0 using Ruby 2.6.3, using Puppet 6.26.0
- PUPPET_GEM_VERSION: 6.26.0
- Platform: CentOS 7.7.1908
Additional Context
With some help I can also contribute a patch. Right now I'm puzzled which plugin destroys the string. We disabled the top_scope_facts plugin because legacy_facts does the same and a little bit more.
Just thinking about it, this is a complex problem. First of all: I think that multiple lint rules trying to make changes to the same code is very likely to produce errors. So the question is how you can detect this.
A lint plugin gets full access to the lexer tokens. It isn't limited to a single token. That means fixes are unpredictable. You'd almost have to mark a token as "I will replace this" or "this was already modified" so that when another lint rule reaches it at least a conflict can be detected.
Another approach is to make "passes". Every lint rule would be a pass, resulting in a new list of tokens. However, it would probably be a major refactor.
I recently pulled in all puppet-lint-plugins into our companys' pdk template and after updating 200+ modules these two are the only conflicting plugins.
My idea for solving this: inside the fix method of both plugins check if the problem token is still causing a warning, and only then apply the fix. So whichever plugin is executed later, will just do nothing.
Hey @ConradGroth . Is that something you could provide a PR for?
Sure. But first I want to see some progress on my open PR against pdk-templates repo. Also the fixes have to go into two repos of community plugins. Are the owners still available or can somebody else merge PR's there?
Are the owners still available or can somebody else merge PR's there?
Could you provide a link?
The two conflicting plugins are https://github.com/mmckinst/puppet-lint-top_scope_facts-check and https://github.com/mmckinst/puppet-lint-legacy_facts-check
This actually appears to be fixed in main
right now. Using the example from OP I got a fix that parsed without any errors.
We will be shipping a new puppet-lint release this week!