Expand drop-in search paths

* top-level (pod.d)
* truncated (unit-.container.d)

Signed-off-by: Bennie Milburn-Town <63211101+benniekiss@users.noreply.github.com>
This commit is contained in:
benniekiss
2024-07-12 13:39:45 -04:00
committed by Bennie Milburn-Town
parent 89432899a7
commit 3c52ef43f5
5 changed files with 182 additions and 25 deletions

View File

@ -939,14 +939,63 @@ func (f *UnitFile) PrependUnitLine(groupName string, key string, value string) {
group.prependLine(newUnitLine(key, value, false))
}
func (f *UnitFile) GetTemplateParts() (string, string) {
func (f *UnitFile) GetTemplateParts() (string, string, bool) {
ext := filepath.Ext(f.Filename)
basename := strings.TrimSuffix(f.Filename, ext)
parts := strings.SplitN(basename, "@", 2)
if len(parts) < 2 {
return "", ""
return parts[0], "", false
}
return parts[0] + "@" + ext, parts[1]
return parts[0], parts[1], true
}
func (f *UnitFile) GetUnitDropinPaths() []string {
unitName, instanceName, isTemplate := f.GetTemplateParts()
ext := filepath.Ext(f.Filename)
dropinExt := ext + ".d"
dropinPaths := []string{}
// Add top-level drop-in location (pod.d, container.d, etc)
topLevelDropIn := strings.TrimPrefix(dropinExt, ".")
dropinPaths = append(dropinPaths, topLevelDropIn)
truncatedParts := strings.Split(unitName, "-")
// If the unit contains any '-', then there are truncated paths to search.
if len(truncatedParts) > 1 {
// We don't need the last item because that would be the full path
truncatedParts = truncatedParts[:len(truncatedParts)-1]
// Truncated instance names are not included in the drop-in search path
// i.e. template-unit@base-instance.service does not search template-unit@base-.service
// So we only search truncations of the template name, i.e. template-@.service, and unit name, i.e. template-.service
// or only the unit name if it is not a template.
for i := range truncatedParts {
truncatedUnitPath := strings.Join(truncatedParts[:i+1], "-") + "-"
dropinPaths = append(dropinPaths, truncatedUnitPath+dropinExt)
// If the unit is a template, add the truncated template name as well.
if isTemplate {
truncatedTemplatePath := truncatedUnitPath + "@"
dropinPaths = append(dropinPaths, truncatedTemplatePath+dropinExt)
}
}
}
// For instanced templates, add the base template unit search path
if instanceName != "" {
dropinPaths = append(dropinPaths, unitName+"@"+dropinExt)
}
// Add the drop-in directory for the full filename
dropinPaths = append(dropinPaths, f.Filename+".d")
// Finally, reverse the list so that when drop-ins are parsed,
// the most specific are applied instead of the most broad.
// dropinPaths should be a list where the items are in order of specific -> broad
// i.e., the most specific search path is dropinPaths[0], and broadest search path is dropinPaths[len(dropinPaths)-1]
// Uses https://go.dev/wiki/SliceTricks#reversing
for i := len(dropinPaths)/2 - 1; i >= 0; i-- {
opp := len(dropinPaths) - 1 - i
dropinPaths[i], dropinPaths[opp] = dropinPaths[opp], dropinPaths[i]
}
return dropinPaths
}
func PathEscape(path string) string {

View File

@ -1,6 +1,7 @@
package parser
import (
"reflect"
"testing"
"github.com/stretchr/testify/assert"
@ -226,6 +227,43 @@ Also=systemd-networkd-wait-online.service
var samples = []string{memcachedService, systemdloginService, systemdnetworkdService}
const sampleDropinService string = "sample-unit.service"
var sampleDropinServicePaths = []string{
"sample-unit.service.d",
"sample-.service.d",
"service.d",
}
const sampleDropinTemplate string = "sample-template-unit@.service"
var sampleDropinTemplatePaths = []string{
"sample-template-unit@.service.d",
"sample-template-@.service.d",
"sample-template-.service.d",
"sample-@.service.d",
"sample-.service.d",
"service.d",
}
const sampleDropinTemplateInstance string = "sample-template-unit@base-instance.service"
var sampleDropinTemplateInstancePaths = []string{
"sample-template-unit@base-instance.service.d",
"sample-template-unit@.service.d",
"sample-template-@.service.d",
"sample-template-.service.d",
"sample-@.service.d",
"sample-.service.d",
"service.d",
}
var sampleDropinPaths = map[string][]string{
sampleDropinService: sampleDropinServicePaths,
sampleDropinTemplate: sampleDropinTemplatePaths,
sampleDropinTemplateInstance: sampleDropinTemplateInstancePaths,
}
func TestRanges_Roundtrip(t *testing.T) {
for i := range samples {
sample := samples[i]
@ -243,3 +281,14 @@ func TestRanges_Roundtrip(t *testing.T) {
assert.Equal(t, sample, asStr)
}
}
func TestUnitDropinPaths_Search(t *testing.T) {
for filename, expectedPaths := range sampleDropinPaths {
f := UnitFile{
Filename: filename,
}
generatedPaths := f.GetUnitDropinPaths()
assert.True(t, reflect.DeepEqual(expectedPaths, generatedPaths))
}
}