catch four more class reference forms
ci/woodpecker/pr/pre-commit Pipeline was canceled
ci/woodpecker/pr/build Pipeline was canceled
ci/woodpecker/pr/test Pipeline was canceled

A params.pp is only ever reached by inherits and $class::var, so changing one
resolved to no hosts at all.

- index inherits, $class::var reads and create_resources()
- match a defined type declared part-way along a line
This commit is contained in:
2026-09-12 16:22:27 +10:00
parent 8093933500
commit 9919676836
9 changed files with 51 additions and 8 deletions
+2 -1
View File
@@ -202,7 +202,8 @@ pluginsynced to every agent and is treated the same way.
Finding what applies a class means searching two places, not one:
1. `.pp` files under `site/` and `modules/` for `include`, `contain`,
`class { '...': }`, capitalised resource references and defined-type usage.
`inherits`, `class { '...': }`, capitalised resource references,
`create_resources()`, `$class::var` reads and defined-type usage.
2. `hieradata/**/*.{yaml,eyaml}` for the class as a list item under
`hiera_include:`. `profiles::base` declares
`($hiera_include - $hiera_exclude).include` with a `unique` merge across the
+11 -6
View File
@@ -17,11 +17,16 @@ import (
var manifestRoots = []string{"site", "modules"}
var (
commentRe = regexp.MustCompile(`(?m)#.*$`)
includeRe = regexp.MustCompile(`\b(?:include|contain|require)\s*\(?\s*['"]?(?:::)?([a-z][a-z0-9_]*(?:::[a-z0-9_]+)*)['"]?`)
classParamRe = regexp.MustCompile(`\bclass\s*\{\s*['"](?:::)?([a-z][a-z0-9_]*(?:::[a-z0-9_]+)*)['"]`)
classRefRe = regexp.MustCompile(`\bClass\s*\[\s*['"](?:::)?([a-z][a-z0-9_]*(?:::[a-z0-9_]+)*)['"]`)
defineUseRe = regexp.MustCompile(`(?m)^\s*(?:::)?([a-z][a-z0-9_]*(?:::[a-z0-9_]+)+)\s*\{`)
commentRe = regexp.MustCompile(`(?m)#.*$`)
includeRe = regexp.MustCompile(`\b(?:include|contain|require|inherits)\s*\(?\s*['"]?(?:::)?([a-z][a-z0-9_]*(?:::[a-z0-9_]+)*)['"]?`)
classParamRe = regexp.MustCompile(`\bclass\s*\{\s*['"](?:::)?([a-z][a-z0-9_]*(?:::[a-z0-9_]+)*)['"]`)
classRefRe = regexp.MustCompile(`\bClass\s*\[\s*['"](?:::)?([a-z][a-z0-9_]*(?:::[a-z0-9_]+)*)['"]`)
defineUseRe = regexp.MustCompile(`(?m)(?:^|[^\w:$.])(?:::)?([a-z][a-z0-9_]*(?:::[a-z0-9_]+)+)\s*\{`)
// $mod::params::thing reads a class variable, which is a dependency on the
// class that declares it.
scopeVarRe = regexp.MustCompile(`\$(?:::)?([a-z][a-z0-9_]*(?:::[a-z0-9_]+)+)::[a-z_][a-z0-9_]*`)
// create_resources('mod::type', ...) names a defined type in a string.
createResRe = regexp.MustCompile(`create_resources\s*\(\s*['"](?:::)?([a-z][a-z0-9_]*(?:::[a-z0-9_]+)*)['"]`)
resourceRefRe = regexp.MustCompile(`\b([A-Z][a-zA-Z0-9_]*(?:::[A-Z][a-zA-Z0-9_]*)+)\s*\[`)
assetRe = regexp.MustCompile(`(?:template|epp|file)\s*\(\s*['"]([a-z0-9_]+/[^'"]+)['"]|puppet:///modules/([a-z0-9_]+/[^'"\s]+)`)
hieraBlockRe = regexp.MustCompile(`(?m)^(hiera_include|hiera_exclude):\s*$((?:\n\s+-\s*\S+)*)`)
@@ -153,7 +158,7 @@ func references(body string) []string {
seen[name] = true
}
}
for _, re := range []*regexp.Regexp{includeRe, classParamRe, classRefRe, defineUseRe} {
for _, re := range []*regexp.Regexp{includeRe, classParamRe, classRefRe, defineUseRe, scopeVarRe, createResRe} {
for _, m := range re.FindAllStringSubmatch(body, -1) {
add(m[1])
}
+20
View File
@@ -123,3 +123,23 @@ func TestParseHieraListsRegexFallback(t *testing.T) {
t.Errorf("exclude = %v", exclude)
}
}
func TestResolveInheritsScopeVarCreateResourcesAndInlineDefine(t *testing.T) {
// Four reference forms a plain include grep misses: class inheritance,
// a $class::var read, create_resources() naming a type in a string, and a
// defined type declared part-way along a line.
tests := map[string]string{
"inherits": "profiles::inherited",
"scope variable": "profiles::scopevar",
"create_resources": "profiles::created",
"inline define": "profiles::inline::thing",
}
want := []string{"k8s1", "k8s2", "web1"}
for name, class := range tests {
t.Run(name, func(t *testing.T) {
if got := resolvedHosts(t, class); !reflect.DeepEqual(got, want) {
t.Errorf("%s = %v, want %v", class, got, want)
}
})
}
}
@@ -0,0 +1,2 @@
define profiles::created () {
}
@@ -0,0 +1,2 @@
class profiles::inherited {
}
@@ -0,0 +1,2 @@
define profiles::inline::thing () {
}
@@ -1,4 +1,12 @@
class profiles::leaf {
$copied = $profiles::scopevar::thing
create_resources('profiles::created', {})
case $copied {
'x': { profiles::inline::thing { 'mid-line': } }
default: {}
}
file { '/tmp/leaf':
content => template('profiles/leaf/x.erb'),
source => 'puppet:///modules/profiles/leaf/y.txt',
@@ -1,3 +1,3 @@
class profiles::mid {
class profiles::mid inherits profiles::inherited {
include profiles::leaf
}
@@ -0,0 +1,3 @@
class profiles::scopevar {
$thing = 'x'
}