Skip to content

Commit

Permalink
MFW-4961: Update w/ filesystem locator. (#394)
Browse files Browse the repository at this point in the history
* Update w/ filesystem locator.

version: bug
  • Loading branch information
john-phillips-arista authored May 15, 2024
1 parent 841c90c commit 04b3d0d
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 13 deletions.
10 changes: 8 additions & 2 deletions services/appclassmanager/appclassmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strconv"

logService "github.com/untangle/golang-shared/services/logger"
"github.com/untangle/golang-shared/services/settings"
)

var logger = logService.GetLoggerInstance()
Expand Down Expand Up @@ -132,9 +133,14 @@ func loadApplicationTable() {
var err error

ApplicationTable = make(map[string]*ApplicationInfo)

filename, err := settings.LocateFile(guidInfoFile)
if err != nil {
logger.Warn("Unable to locate GUID info file: %s\n",
guidInfoFile)
return
}
// open the guid info file provided by Sandvine
file, err = os.Open(guidInfoFile)
file, err = os.Open(filename)

// if there was an error log and return
if err != nil {
Expand Down
84 changes: 84 additions & 0 deletions services/settings/filesystem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package settings

import (
"fmt"
"os"
"path/filepath"
"strings"
)

// FilenameLocator finds files on the local filesytem, allowing the
// system to be in hybrid or non-hybid mode and concealing the
// diffrences.
type FilenameLocator struct {
fileExists func(filename string) bool
}

const (
// prefix for generic filepaths in hybrid mode
hybridModeGenericPrefix = "/mfw"

// kernel forwarding mode/BST container mode path prefix.
kernelModeSettingsPrefix = "/etc/config"

// prefix specifically for config files in hybrid mode
hybridModeSettingsPrefix = "/mnt/flash/mfw-settings"
)

// FileExists returns true if we can Stat the filename. We don't
// distinguish between various kinds of errors, but do log them, on
// the theory that if you can't Stat the filename, for most purposes,
// that is the same as it not existing, and isn't a common case.
func FileExists(fname string) bool {
if _, err := os.Stat(fname); err != nil {
if !os.IsNotExist(err) {
logger.Warn("Unexpected error code from os.Stat: %s",
err)
}
return false
}
return true
}

func (f *FilenameLocator) findEOSFileName(filename string) (string, error) {
if strings.HasPrefix(filename, kernelModeSettingsPrefix) {
newFileName := strings.Replace(
filename,
kernelModeSettingsPrefix,
hybridModeSettingsPrefix,
1)
if !f.fileExists(newFileName) {
return "", fmt.Errorf("unable to find config file: %s", filename)
}
return newFileName, nil
} else {
newFileName := filepath.Join(
hybridModeGenericPrefix,
filename)
if !f.fileExists(newFileName) {
return "", fmt.Errorf(
"unable to locate file: %s", filename)
}
return newFileName, nil
}
}

// LocateFile locates the input filename on the filesystem,
// automatically translating it to hybrid mode filenames when needed.
func (f *FilenameLocator) LocateFile(filename string) (string, error) {
if f.fileExists(filename) {
return filename, nil
}
return f.findEOSFileName(filename)

}

var defaultLocator = &FilenameLocator{
fileExists: FileExists,
}

// LocateFile calls FilenameLocator.LocateFile on the default filename
// locator.
func LocateFile(filename string) (string, error) {
return defaultLocator.LocateFile(filename)
}
85 changes: 85 additions & 0 deletions services/settings/filesystem_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package settings

import (
"fmt"
"os"
"testing"

"github.com/stretchr/testify/assert"
"github.com/untangle/golang-shared/testing/mocks"
)

type fileExistsFake struct {
rvals []bool
}

func (f *fileExistsFake) doesExist(fname string) bool {
rval := f.rvals[0]
f.rvals = f.rvals[1:]
return rval
}
func TestFilenameLocator(t *testing.T) {
existFake := &fileExistsFake{}
locator := FilenameLocator{
fileExists: existFake.doesExist}
tests := []struct {
filename string
existResults []bool
returnValue string
returnErr error
}{
{
filename: "/etc/config/settings.json",
existResults: []bool{false, true},
returnValue: "/mnt/flash/mfw-settings/settings.json",
},
{
filename: "/usr/share/geoip",
existResults: []bool{false, true},
returnValue: "/mfw/usr/share/geoip",
},
{
filename: "/etc/config/appstate.json",
existResults: []bool{false, true},
returnValue: "/mnt/flash/mfw-settings/appstate.json",
},
{
filename: "/etc/config/settings.json",
existResults: []bool{true, true},
returnValue: "/etc/config/settings.json",
},
{
filename: "/etc/config/appstate.json",
existResults: []bool{true, true},
returnValue: "/etc/config/appstate.json",
},
{
filename: "/etc/config/appstate.json",
existResults: []bool{false, false},
returnValue: "",
returnErr: fmt.Errorf("/etc/config/appstate.json"),
},
}

for _, test := range tests {
existFake.rvals = test.existResults
result, err := locator.LocateFile(test.filename)
assert.Equal(t, result, test.returnValue)
if test.returnErr == nil {
assert.NoError(t, err)
} else {
assert.Regexp(t, test.returnErr.Error(), err.Error(),
"errors should match")
}
}

}

func TestFileExists(t *testing.T) {
thisFile, err := os.Executable()
logger = mocks.NewMockLogger()
assert.NoError(t, err)
assert.True(t, FileExists(thisFile))
assert.False(t,
FileExists("/some-file/that-should/definitely-not/exist-anywhere"))
}
39 changes: 32 additions & 7 deletions services/settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,27 @@ import (
"github.com/untangle/golang-shared/plugins/util"
)

// TODO: fix this, we should not rely on people happening to call Startup().
var logger loggerModel.LoggerLevels
var once sync.Once

const settingsFile = "/etc/config/settings.json"
const defaultsFile = "/etc/config/defaults.json"
const currentFile = "/etc/config/current.json"
// find the file or fallback to the old filename if we can't.
func locateOrDefault(filename string) string {
// useing fmt.Fprintf because of the above logger var.
if filename, err := LocateFile(filename); err == nil {
return filename
}
fmt.Fprintf(os.Stderr,
"settings: Unable to locate: %s, defaulting...",
filename)
return filename
}

var (
settingsFile = locateOrDefault("/etc/config/settings.json")
defaultsFile = locateOrDefault("/etc/config/defaults.json")
currentFile = locateOrDefault("/etc/config/current.json")
)

var syncCallbacks []func()

Expand Down Expand Up @@ -64,9 +79,18 @@ var settingsFileSingleton *SettingsFile
// singleton. Prefer using this if you can.
func GetSettingsFileSingleton() *SettingsFile {
if settingsFileSingleton == nil {
settingsFileSingleton = NewSettingsFile(
settingsFile,
WithLock(&saveLocker))
if fileName, err := LocateFile(settingsFile); err == nil {
settingsFileSingleton = NewSettingsFile(
fileName,
WithLock(&saveLocker))
} else {
fmt.Fprintf(os.Stderr,
"Unable to locate settings file, falling back to %s and hoping for the best...\n",
settingsFile)
settingsFileSingleton = NewSettingsFile(
settingsFile,
WithLock(&saveLocker))
}
}
return settingsFileSingleton
}
Expand All @@ -77,6 +101,7 @@ func GetCurrentSettings(segments []string) (interface{}, error) {
// for backwards compatibility before we saved current.json
// if it does not exist, just read settings.json
// XXX this should be removed at some point in the future

if _, err := os.Stat(currentFile); os.IsNotExist(err) {
return GetSettingsFile(segments, settingsFile)
}
Expand Down Expand Up @@ -597,7 +622,7 @@ func tempFile(dir, pattern string) (f *os.File, err error) {

// GetUIDOpenwrt returns the UID of the system
func GetUIDOpenwrt() (string, error) {
return GetUID("/etc/config/uid")
return GetUID(locateOrDefault("/etc/config/uid"))
}

// GetUID returns the UID of the system
Expand Down
7 changes: 3 additions & 4 deletions util/net/interfaces/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@ func (ifaces *InterfaceSettings) SetJsonPath(jsonPath ...string) {
}

const (
defaultSettingsFile = "/etc/config/settings.json"
defaultJsonParent = "network"
defaultJsonChild = "interfaces"
defaultJsonParent = "network"
defaultJsonChild = "interfaces"
)

// NewInterfaceSettings returns an InterfaceSettings object which uses
Expand Down Expand Up @@ -115,7 +114,7 @@ func GetLocalInterfaces() []Interface {

func GetDefaultInterfaceSettings() InterfaceSettings {
intfSettings := InterfaceSettings{
file: settings.NewSettingsFile(defaultSettingsFile),
file: settings.GetSettingsFileSingleton(),
jsonPath: []string{},
}
intfSettings.SetJsonPath(defaultJsonParent, defaultJsonChild)
Expand Down

0 comments on commit 04b3d0d

Please sign in to comment.