mirror of
https://github.com/portainer/portainer.git
synced 2025-07-19 13:29:41 +02:00
feat(edgeconfigs): parse .env config files for interpolation BE-11673 (#514)
This commit is contained in:
parent
66bcf9223a
commit
2c05496962
2 changed files with 96 additions and 70 deletions
|
@ -15,15 +15,19 @@ type MultiFilterArgs []struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MultiFilterDirForPerDevConfigs filers the given dirEntries with multiple filter args, returns the merged entries for the given device
|
// MultiFilterDirForPerDevConfigs filers the given dirEntries with multiple filter args, returns the merged entries for the given device
|
||||||
func MultiFilterDirForPerDevConfigs(dirEntries []DirEntry, configPath string, multiFilterArgs MultiFilterArgs) []DirEntry {
|
func MultiFilterDirForPerDevConfigs(dirEntries []DirEntry, configPath string, multiFilterArgs MultiFilterArgs) ([]DirEntry, []string) {
|
||||||
var filteredDirEntries []DirEntry
|
var filteredDirEntries []DirEntry
|
||||||
|
|
||||||
|
var envFiles []string
|
||||||
|
|
||||||
for _, multiFilterArg := range multiFilterArgs {
|
for _, multiFilterArg := range multiFilterArgs {
|
||||||
tmp := FilterDirForPerDevConfigs(dirEntries, multiFilterArg.FilterKey, configPath, multiFilterArg.FilterType)
|
tmp, efs := FilterDirForPerDevConfigs(dirEntries, multiFilterArg.FilterKey, configPath, multiFilterArg.FilterType)
|
||||||
filteredDirEntries = append(filteredDirEntries, tmp...)
|
filteredDirEntries = append(filteredDirEntries, tmp...)
|
||||||
|
|
||||||
|
envFiles = append(envFiles, efs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return deduplicate(filteredDirEntries)
|
return deduplicate(filteredDirEntries), envFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
func deduplicate(dirEntries []DirEntry) []DirEntry {
|
func deduplicate(dirEntries []DirEntry) []DirEntry {
|
||||||
|
@ -32,8 +36,7 @@ func deduplicate(dirEntries []DirEntry) []DirEntry {
|
||||||
marks := make(map[string]struct{})
|
marks := make(map[string]struct{})
|
||||||
|
|
||||||
for _, dirEntry := range dirEntries {
|
for _, dirEntry := range dirEntries {
|
||||||
_, ok := marks[dirEntry.Name]
|
if _, ok := marks[dirEntry.Name]; !ok {
|
||||||
if !ok {
|
|
||||||
marks[dirEntry.Name] = struct{}{}
|
marks[dirEntry.Name] = struct{}{}
|
||||||
deduplicatedDirEntries = append(deduplicatedDirEntries, dirEntry)
|
deduplicatedDirEntries = append(deduplicatedDirEntries, dirEntry)
|
||||||
}
|
}
|
||||||
|
@ -50,20 +53,25 @@ func deduplicate(dirEntries []DirEntry) []DirEntry {
|
||||||
// 3. For filterType dir:
|
// 3. For filterType dir:
|
||||||
// dir entry: A/B/C/<deviceName>
|
// dir entry: A/B/C/<deviceName>
|
||||||
// all entries: A/B/C/<deviceName>/*
|
// all entries: A/B/C/<deviceName>/*
|
||||||
func FilterDirForPerDevConfigs(dirEntries []DirEntry, deviceName, configPath string, filterType portainer.PerDevConfigsFilterType) []DirEntry {
|
func FilterDirForPerDevConfigs(dirEntries []DirEntry, deviceName, configPath string, filterType portainer.PerDevConfigsFilterType) ([]DirEntry, []string) {
|
||||||
var filteredDirEntries []DirEntry
|
var filteredDirEntries []DirEntry
|
||||||
|
|
||||||
|
var envFiles []string
|
||||||
|
|
||||||
for _, dirEntry := range dirEntries {
|
for _, dirEntry := range dirEntries {
|
||||||
if shouldIncludeEntry(dirEntry, deviceName, configPath, filterType) {
|
if shouldIncludeEntry(dirEntry, deviceName, configPath, filterType) {
|
||||||
filteredDirEntries = append(filteredDirEntries, dirEntry)
|
filteredDirEntries = append(filteredDirEntries, dirEntry)
|
||||||
|
|
||||||
|
if shouldParseEnvVars(dirEntry, deviceName, configPath, filterType) {
|
||||||
|
envFiles = append(envFiles, dirEntry.Name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return filteredDirEntries
|
return filteredDirEntries, envFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
func shouldIncludeEntry(dirEntry DirEntry, deviceName, configPath string, filterType portainer.PerDevConfigsFilterType) bool {
|
func shouldIncludeEntry(dirEntry DirEntry, deviceName, configPath string, filterType portainer.PerDevConfigsFilterType) bool {
|
||||||
|
|
||||||
// Include all entries outside of dir A
|
// Include all entries outside of dir A
|
||||||
if !isInConfigDir(dirEntry, configPath) {
|
if !isInConfigDir(dirEntry, configPath) {
|
||||||
return true
|
return true
|
||||||
|
@ -120,6 +128,15 @@ func shouldIncludeDir(dirEntry DirEntry, deviceName, configPath string) bool {
|
||||||
return strings.HasPrefix(dirEntry.Name, filterPrefix)
|
return strings.HasPrefix(dirEntry.Name, filterPrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shouldParseEnvVars(dirEntry DirEntry, deviceName, configPath string, filterType portainer.PerDevConfigsFilterType) bool {
|
||||||
|
if !dirEntry.IsFile {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return isInConfigDir(dirEntry, configPath) &&
|
||||||
|
filepath.Base(dirEntry.Name) == deviceName+".env"
|
||||||
|
}
|
||||||
|
|
||||||
func appendTailSeparator(path string) string {
|
func appendTailSeparator(path string) string {
|
||||||
return fmt.Sprintf("%s%c", path, os.PathSeparator)
|
return fmt.Sprintf("%s%c", path, os.PathSeparator)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,17 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
portainer "github.com/portainer/portainer/api"
|
portainer "github.com/portainer/portainer/api"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMultiFilterDirForPerDevConfigs(t *testing.T) {
|
func TestMultiFilterDirForPerDevConfigs(t *testing.T) {
|
||||||
type args struct {
|
f := func(dirEntries []DirEntry, configPath string, multiFilterArgs MultiFilterArgs, wantDirEntries []DirEntry) {
|
||||||
dirEntries []DirEntry
|
t.Helper()
|
||||||
configPath string
|
|
||||||
multiFilterArgs MultiFilterArgs
|
dirEntries, _ = MultiFilterDirForPerDevConfigs(dirEntries, configPath, multiFilterArgs)
|
||||||
|
require.Equal(t, wantDirEntries, dirEntries)
|
||||||
}
|
}
|
||||||
|
|
||||||
baseDirEntries := []DirEntry{
|
baseDirEntries := []DirEntry{
|
||||||
|
@ -26,69 +29,75 @@ func TestMultiFilterDirForPerDevConfigs(t *testing.T) {
|
||||||
{"configs/folder2/config2", "", true, 420},
|
{"configs/folder2/config2", "", true, 420},
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
// Filter file1
|
||||||
name string
|
f(
|
||||||
args args
|
baseDirEntries,
|
||||||
want []DirEntry
|
"configs",
|
||||||
}{
|
MultiFilterArgs{{"file1", portainer.PerDevConfigsTypeFile}},
|
||||||
{
|
[]DirEntry{baseDirEntries[0], baseDirEntries[1], baseDirEntries[2], baseDirEntries[3]},
|
||||||
name: "filter file1",
|
)
|
||||||
args: args{
|
|
||||||
baseDirEntries,
|
// Filter folder1
|
||||||
"configs",
|
f(
|
||||||
MultiFilterArgs{{"file1", portainer.PerDevConfigsTypeFile}},
|
baseDirEntries,
|
||||||
},
|
"configs",
|
||||||
want: []DirEntry{baseDirEntries[0], baseDirEntries[1], baseDirEntries[2], baseDirEntries[3]},
|
MultiFilterArgs{{"folder1", portainer.PerDevConfigsTypeDir}},
|
||||||
|
[]DirEntry{baseDirEntries[0], baseDirEntries[1], baseDirEntries[2], baseDirEntries[5], baseDirEntries[6]},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Filter file1 and folder1
|
||||||
|
f(
|
||||||
|
baseDirEntries,
|
||||||
|
"configs",
|
||||||
|
MultiFilterArgs{{"folder1", portainer.PerDevConfigsTypeDir}},
|
||||||
|
[]DirEntry{baseDirEntries[0], baseDirEntries[1], baseDirEntries[2], baseDirEntries[5], baseDirEntries[6]},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Filter file1 and file2
|
||||||
|
f(
|
||||||
|
baseDirEntries,
|
||||||
|
"configs",
|
||||||
|
MultiFilterArgs{
|
||||||
|
{"file1", portainer.PerDevConfigsTypeFile},
|
||||||
|
{"file2", portainer.PerDevConfigsTypeFile},
|
||||||
},
|
},
|
||||||
{
|
[]DirEntry{baseDirEntries[0], baseDirEntries[1], baseDirEntries[2], baseDirEntries[3], baseDirEntries[4]},
|
||||||
name: "filter folder1",
|
)
|
||||||
args: args{
|
|
||||||
baseDirEntries,
|
// Filter folder1 and folder2
|
||||||
"configs",
|
f(
|
||||||
MultiFilterArgs{{"folder1", portainer.PerDevConfigsTypeDir}},
|
baseDirEntries,
|
||||||
},
|
"configs",
|
||||||
want: []DirEntry{baseDirEntries[0], baseDirEntries[1], baseDirEntries[2], baseDirEntries[5], baseDirEntries[6]},
|
MultiFilterArgs{
|
||||||
},
|
{"folder1", portainer.PerDevConfigsTypeDir},
|
||||||
{
|
{"folder2", portainer.PerDevConfigsTypeDir},
|
||||||
name: "filter file1 and folder1",
|
|
||||||
args: args{
|
|
||||||
baseDirEntries,
|
|
||||||
"configs",
|
|
||||||
MultiFilterArgs{{"folder1", portainer.PerDevConfigsTypeDir}},
|
|
||||||
},
|
|
||||||
want: []DirEntry{baseDirEntries[0], baseDirEntries[1], baseDirEntries[2], baseDirEntries[5], baseDirEntries[6]},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "filter file1 and file2",
|
|
||||||
args: args{
|
|
||||||
baseDirEntries,
|
|
||||||
"configs",
|
|
||||||
MultiFilterArgs{
|
|
||||||
{"file1", portainer.PerDevConfigsTypeFile},
|
|
||||||
{"file2", portainer.PerDevConfigsTypeFile},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: []DirEntry{baseDirEntries[0], baseDirEntries[1], baseDirEntries[2], baseDirEntries[3], baseDirEntries[4]},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "filter folder1 and folder2",
|
|
||||||
args: args{
|
|
||||||
baseDirEntries,
|
|
||||||
"configs",
|
|
||||||
MultiFilterArgs{
|
|
||||||
{"folder1", portainer.PerDevConfigsTypeDir},
|
|
||||||
{"folder2", portainer.PerDevConfigsTypeDir},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: []DirEntry{baseDirEntries[0], baseDirEntries[1], baseDirEntries[2], baseDirEntries[5], baseDirEntries[6], baseDirEntries[7], baseDirEntries[8]},
|
|
||||||
},
|
},
|
||||||
|
[]DirEntry{baseDirEntries[0], baseDirEntries[1], baseDirEntries[2], baseDirEntries[5], baseDirEntries[6], baseDirEntries[7], baseDirEntries[8]},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiFilterDirForPerDevConfigsEnvFiles(t *testing.T) {
|
||||||
|
f := func(dirEntries []DirEntry, configPath string, multiFilterArgs MultiFilterArgs, wantEnvFiles []string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
_, envFiles := MultiFilterDirForPerDevConfigs(dirEntries, configPath, multiFilterArgs)
|
||||||
|
require.Equal(t, wantEnvFiles, envFiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
baseDirEntries := []DirEntry{
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
{".env", "", true, 420},
|
||||||
assert.Equalf(t, tt.want, MultiFilterDirForPerDevConfigs(tt.args.dirEntries, tt.args.configPath, tt.args.multiFilterArgs), "MultiFilterDirForPerDevConfigs(%v, %v, %v)", tt.args.dirEntries, tt.args.configPath, tt.args.multiFilterArgs)
|
{"docker-compose.yaml", "", true, 420},
|
||||||
})
|
{"configs", "", false, 420},
|
||||||
|
{"configs/edge-id/edge-id.env", "", true, 420},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f(
|
||||||
|
baseDirEntries,
|
||||||
|
"configs",
|
||||||
|
MultiFilterArgs{{"edge-id", portainer.PerDevConfigsTypeDir}},
|
||||||
|
[]string{"configs/edge-id/edge-id.env"},
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsInConfigDir(t *testing.T) {
|
func TestIsInConfigDir(t *testing.T) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue