puppetlabs / puppet-lint

Check that your Puppet manifests conform to the style guide

Home Page:https://puppetlabs.github.io/puppet-lint/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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?

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!