From 54112b56f2434f9c180e7fac2ce54e7d8ce0722f Mon Sep 17 00:00:00 2001 From: cmeng Date: Thu, 21 Sep 2023 11:22:44 +1200 Subject: [PATCH] feat(edge-config): support edge config for group EE-5962 (#10329) --- api/filesystem/serialize.go | 2 +- api/filesystem/serialize_per_dev_configs.go | 33 +++++++ .../serialize_per_dev_configs_test.go | 91 +++++++++++++++++++ 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 api/filesystem/serialize_per_dev_configs_test.go diff --git a/api/filesystem/serialize.go b/api/filesystem/serialize.go index f37f5aa99..084b82db9 100644 --- a/api/filesystem/serialize.go +++ b/api/filesystem/serialize.go @@ -49,8 +49,8 @@ func FilterDirForEntryFile(dirEntries []DirEntry, entryFile string) []DirEntry { return filteredDirEntries } +// FilterDirForCompatibility returns the content of the entry file if agent version is less than 2.19.0 func FilterDirForCompatibility(dirEntries []DirEntry, entryFilePath, agentVersion string) (string, error) { - if semver.Compare(fmt.Sprintf("v%s", agentVersion), "v2.19.0") == -1 { for _, dirEntry := range dirEntries { if dirEntry.IsFile { diff --git a/api/filesystem/serialize_per_dev_configs.go b/api/filesystem/serialize_per_dev_configs.go index 241ecb0ba..b46637c98 100644 --- a/api/filesystem/serialize_per_dev_configs.go +++ b/api/filesystem/serialize_per_dev_configs.go @@ -9,6 +9,39 @@ import ( "github.com/portainer/portainer/api" ) +type MultiFilterArgs []struct { + FilterKey string + FilterType portainer.PerDevConfigsFilterType +} + +// 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 { + var filteredDirEntries []DirEntry + + for _, multiFilterArg := range multiFilterArgs { + tmp := FilterDirForPerDevConfigs(dirEntries, multiFilterArg.FilterKey, configPath, multiFilterArg.FilterType) + filteredDirEntries = append(filteredDirEntries, tmp...) + } + + return deduplicate(filteredDirEntries) +} + +func deduplicate(dirEntries []DirEntry) []DirEntry { + var deduplicatedDirEntries []DirEntry + + marks := make(map[string]struct{}) + + for _, dirEntry := range dirEntries { + _, ok := marks[dirEntry.Name] + if !ok { + marks[dirEntry.Name] = struct{}{} + deduplicatedDirEntries = append(deduplicatedDirEntries, dirEntry) + } + } + + return deduplicatedDirEntries +} + // FilterDirForPerDevConfigs filers the given dirEntries, returns entries for the given device // For given configPath A/B/C, return entries: // 1. all entries outside of dir A diff --git a/api/filesystem/serialize_per_dev_configs_test.go b/api/filesystem/serialize_per_dev_configs_test.go new file mode 100644 index 000000000..394d39a14 --- /dev/null +++ b/api/filesystem/serialize_per_dev_configs_test.go @@ -0,0 +1,91 @@ +package filesystem + +import ( + portainer "github.com/portainer/portainer/api" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestMultiFilterDirForPerDevConfigs(t *testing.T) { + type args struct { + dirEntries []DirEntry + configPath string + multiFilterArgs MultiFilterArgs + } + + baseDirEntries := []DirEntry{ + {".env", "", true, 420}, + {"docker-compose.yaml", "", true, 420}, + {"configs", "", false, 420}, + {"configs/file1.conf", "", true, 420}, + {"configs/file2.conf", "", true, 420}, + {"configs/folder1", "", false, 420}, + {"configs/folder1/config1", "", true, 420}, + {"configs/folder2", "", false, 420}, + {"configs/folder2/config2", "", true, 420}, + } + + tests := []struct { + name string + args args + want []DirEntry + }{ + { + name: "filter file1", + args: args{ + baseDirEntries, + "configs", + MultiFilterArgs{{"file1", portainer.PerDevConfigsTypeFile}}, + }, + want: []DirEntry{baseDirEntries[0], baseDirEntries[1], baseDirEntries[2], baseDirEntries[3]}, + }, + { + name: "filter 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 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]}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + 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) + }) + } +}