portainer/portainer

View on GitHub
api/filesystem/serialize.go

Summary

Maintainability
A
50 mins
Test Coverage
package filesystem

import (
    "encoding/base64"
    "fmt"
    "os"
    "path/filepath"
    "strings"

    "golang.org/x/mod/semver"
)

type DirEntry struct {
    Name        string
    Content     string
    IsFile      bool
    Permissions os.FileMode
}

// FilterDirForEntryFile filers the given dirEntries, returns entries of the entryFile and .env file
func FilterDirForEntryFile(dirEntries []DirEntry, entryFile string) []DirEntry {
    var filteredDirEntries []DirEntry

    dotEnvFile := filepath.Join(filepath.Dir(entryFile), ".env")
    filters := []string{entryFile, dotEnvFile}

    for _, dirEntry := range dirEntries {
        match := false
        if dirEntry.IsFile {
            for _, filter := range filters {
                if filter == dirEntry.Name {
                    match = true
                    break
                }
            }
        } else {
            for _, filter := range filters {
                if strings.HasPrefix(filter, dirEntry.Name) {
                    match = true
                    break
                }
            }
        }
        if match {
            filteredDirEntries = append(filteredDirEntries, 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 {
                if dirEntry.Name == entryFilePath {
                    return DecodeFileContent(dirEntry.Content)
                }
            }
        }
        return "", fmt.Errorf("Entry file %s not found in dir entries", entryFilePath)
    }

    return "", nil
}

// LoadDir reads all files and folders recursively from the given directory
// File content is base64-encoded
func LoadDir(dir string) ([]DirEntry, error) {
    var dirEntries []DirEntry

    err := filepath.WalkDir(
        dir,
        func(path string, d os.DirEntry, err error) error {
            if err != nil {
                return err
            }

            fileInfo, err := d.Info()
            if err != nil {
                return err
            }

            relativePath, err := filepath.Rel(dir, path)
            if err != nil {
                return err
            }
            if relativePath == "." {
                return nil
            }

            dirEntry := DirEntry{
                Name:        relativePath,
                Permissions: fileInfo.Mode().Perm(),
            }

            if !fileInfo.IsDir() {
                // Read file contents
                fileContent, err := os.ReadFile(path)
                if err != nil {
                    return err
                }

                dirEntry.Content = base64.StdEncoding.EncodeToString(fileContent)
                dirEntry.IsFile = true
            }

            dirEntries = append(dirEntries, dirEntry)
            return nil
        })

    if err != nil {
        return nil, err
    }

    return dirEntries, nil
}

// PersistDir writes the provided array of files and folders back to the given directory.
func PersistDir(dir string, dirEntries []DirEntry) error {
    for _, dirEntry := range dirEntries {
        path := filepath.Join(dir, dirEntry.Name)

        if dirEntry.IsFile {
            // Create the directory path if it doesn't exist
            err := os.MkdirAll(filepath.Dir(path), 0744)
            if err != nil {
                return err
            }

            // Write file contents
            err = os.WriteFile(path, []byte(dirEntry.Content), dirEntry.Permissions)
            if err != nil {
                return err
            }
        } else {
            // Create the directory
            err := os.MkdirAll(path, dirEntry.Permissions)
            if err != nil {
                return err
            }
        }
    }

    return nil
}

func DecodeFileContent(encodedFileContent string) (string, error) {
    decodedBytes, err := base64.StdEncoding.DecodeString(encodedFileContent)
    if err != nil {
        return "", err
    }
    return string(decodedBytes), nil
}

func DecodeDirEntries(dirEntries []DirEntry) error {
    for index, dirEntry := range dirEntries {
        if dirEntry.IsFile && dirEntry.Content != "" {
            decodedFile, err := DecodeFileContent(dirEntry.Content)
            if err != nil {
                return err
            }
            dirEntries[index].Content = decodedFile
        }
    }

    return nil
}