-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: getter of kernel and NFS mount config
Signed-off-by: Raphanus Lo <[email protected]>
- Loading branch information
1 parent
4d6c3a8
commit b60ef86
Showing
7 changed files
with
468 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package nfs | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
|
||
"github.com/longhorn/go-common-libs/types" | ||
) | ||
|
||
const ( | ||
nfsmountGlobalSection = "NFSMount_Global_Options" | ||
nfsmountDefaultVersion = "Defaultvers" | ||
) | ||
|
||
// GetSystemDefaultNFSVersion reads the system default NFS version. This config can be overridden by nfsmount.conf under | ||
// configDir. If configDir is empty, it will be /etc by default. If the nfsmount.conf is absent, or the default | ||
// version is not overridden in the configuration file, a wrapped ErrNotConfigured error will be returned. It is possible | ||
// for the caller to tell if the configuration is overridden by errors.Is(err, types.ErrNotConfigured). | ||
func GetSystemDefaultNFSVersion(configDir string) (major, minor int, err error) { | ||
if configDir == "" { | ||
configDir = types.SysEtcDirectory | ||
} | ||
|
||
configMap, err := getSystemNFSMountConfigMap(configDir) | ||
if os.IsNotExist(err) { | ||
return 0, 0, fmt.Errorf("system default NFS version is not overridden under %q: %w", configDir, types.ErrNotConfigured) | ||
} else if err != nil { | ||
return 0, 0, err | ||
} | ||
if globalSection, ok := configMap[nfsmountGlobalSection]; ok { | ||
if configured, ok := globalSection[nfsmountDefaultVersion]; ok { | ||
majorStr, minorStr, _ := strings.Cut(configured, ".") | ||
major, err := strconv.Atoi(majorStr) | ||
if err != nil { | ||
return 0, 0, errors.Wrapf(err, "invalid NFS major version %q", configured) | ||
} | ||
|
||
minor := 0 | ||
if minorStr != "" { | ||
minor, err = strconv.Atoi(minorStr) | ||
if err != nil { | ||
return 0, 0, errors.Wrapf(err, "invalid NFS minor version %q", configured) | ||
} | ||
} | ||
return major, minor, nil | ||
} | ||
} | ||
return 0, 0, fmt.Errorf("system default NFS version is not overridden under %q: %w", configDir, types.ErrNotConfigured) | ||
} | ||
|
||
// getSystemNFSMountConfigMap reads the nfsmount.conf under configDir. The returned result is in | ||
// map[section]map[key]value structure, and the global options is result["NFSMount_Global_Options"]. Refer to man page | ||
// for more detail: man 5 nfsmount.conf | ||
func getSystemNFSMountConfigMap(configDir string) (map[string]map[string]string, error) { | ||
configFilePath := filepath.Join(configDir, types.NFSMountFileName) | ||
configFile, err := os.Open(configFilePath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer configFile.Close() | ||
|
||
configMap := make(map[string]map[string]string) | ||
scanner := bufio.NewScanner(configFile) | ||
var section = "" | ||
var sectionMap map[string]string | ||
|
||
for scanner.Scan() { | ||
line := strings.TrimSpace(scanner.Text()) | ||
if line == "" || strings.HasPrefix(line, "#") { | ||
continue | ||
} | ||
if strings.HasPrefix(line, "[") { | ||
section = strings.TrimSpace(strings.Trim(line, "[]")) | ||
continue | ||
} | ||
if sectionMap = configMap[section]; sectionMap == nil { | ||
sectionMap = make(map[string]string) | ||
configMap[section] = sectionMap | ||
} | ||
if key, val, isParsable := strings.Cut(line, "="); isParsable { | ||
sectionMap[strings.TrimSpace(key)] = strings.TrimSpace(val) | ||
} else { | ||
return nil, fmt.Errorf("invalid key-value pair: '%s'", line) | ||
} | ||
} | ||
|
||
if err := scanner.Err(); err != nil { | ||
return nil, err | ||
} | ||
return configMap, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package nfs | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
. "gopkg.in/check.v1" | ||
|
||
"github.com/longhorn/go-common-libs/types" | ||
|
||
"github.com/longhorn/go-common-libs/test" | ||
) | ||
|
||
func Test(t *testing.T) { TestingT(t) } | ||
|
||
type TestSuite struct{} | ||
|
||
var _ = Suite(&TestSuite{}) | ||
|
||
func (s *TestSuite) TestGetSystemDefaultNFSVersion(c *C) { | ||
genNFSMountConf := func(dir string, nfsVer string) { | ||
data := fmt.Sprintf("[ NFSMount_Global_Options ]\nDefaultvers=%s\n", nfsVer) | ||
err := os.WriteFile(filepath.Join(dir, types.NFSMountFileName), []byte(data), 0644) | ||
c.Assert(err, IsNil) | ||
} | ||
|
||
type testCase struct { | ||
setup func(dir string) | ||
expectedMajor int | ||
expectedMinor int | ||
expectedMissing bool | ||
expectedError bool | ||
} | ||
testCases := map[string]testCase{ | ||
"GetSystemDefaultNFSVersion(...): system NFS mount config absent": { | ||
setup: func(dir string) { | ||
_, err := os.Stat(filepath.Join(dir, types.NFSMountFileName)) | ||
c.Assert(os.IsNotExist(err), Equals, true) | ||
}, | ||
expectedMajor: 0, | ||
expectedMinor: 0, | ||
expectedMissing: true, | ||
expectedError: true, | ||
}, | ||
"GetSystemDefaultNFSVersion(...): system NFS mount config exist without default ver": { | ||
setup: func(dir string) { | ||
data := "[ NFSMount_Global_Options ]\notherkey=val\n" | ||
err := os.WriteFile(filepath.Join(dir, types.NFSMountFileName), []byte(data), 0644) | ||
c.Assert(err, IsNil) | ||
}, | ||
expectedMajor: 0, | ||
expectedMinor: 0, | ||
expectedMissing: true, | ||
expectedError: true, | ||
}, | ||
"GetSystemDefaultNFSVersion(...): system NFS mount config set default ver to 3": { | ||
setup: func(dir string) { genNFSMountConf(dir, "3") }, | ||
expectedMajor: 3, | ||
expectedMinor: 0, | ||
expectedMissing: false, | ||
expectedError: false, | ||
}, | ||
"GetSystemDefaultNFSVersion(...): system NFS mount config set default ver to 4": { | ||
setup: func(dir string) { genNFSMountConf(dir, "4") }, | ||
expectedMajor: 4, | ||
expectedMinor: 0, | ||
expectedMissing: false, | ||
expectedError: false, | ||
}, | ||
"GetSystemDefaultNFSVersion(...): system NFS mount config set default ver to 4.0": { | ||
setup: func(dir string) { genNFSMountConf(dir, "4.0") }, | ||
expectedMajor: 4, | ||
expectedMinor: 0, | ||
expectedMissing: false, | ||
expectedError: false, | ||
}, | ||
"GetSystemDefaultNFSVersion(...): system NFS mount config set default ver to 4.2": { | ||
setup: func(dir string) { genNFSMountConf(dir, "4.2") }, | ||
expectedMajor: 4, | ||
expectedMinor: 2, | ||
expectedMissing: false, | ||
expectedError: false, | ||
}, | ||
"GetSystemDefaultNFSVersion(...): system NFS mount config default ver to invalid value": { | ||
setup: func(dir string) { genNFSMountConf(dir, "???") }, | ||
expectedMajor: 0, | ||
expectedMinor: 0, | ||
expectedMissing: false, | ||
expectedError: true, | ||
}, | ||
"GetSystemDefaultNFSVersion(...): system NFS mount config default ver to empty value": { | ||
setup: func(dir string) { genNFSMountConf(dir, "") }, | ||
expectedMajor: 0, | ||
expectedMinor: 0, | ||
expectedMissing: false, | ||
expectedError: true, | ||
}, | ||
} | ||
|
||
configDir := c.MkDir() | ||
|
||
for testName, testCase := range testCases { | ||
func() { | ||
c.Logf("testing utils.%v", testName) | ||
|
||
defer os.Remove(filepath.Join(configDir, types.NFSMountFileName)) | ||
testCase.setup(configDir) | ||
|
||
major, minor, err := GetSystemDefaultNFSVersion(configDir) | ||
c.Assert(major, Equals, testCase.expectedMajor, Commentf(test.ErrResultFmt, testName)) | ||
c.Assert(minor, Equals, testCase.expectedMinor, Commentf(test.ErrResultFmt, testName)) | ||
if testCase.expectedMissing { | ||
c.Assert(errors.Is(err, types.ErrNotConfigured), Equals, true, Commentf(test.ErrResultFmt, testName)) | ||
} else if testCase.expectedError { | ||
c.Assert(err, NotNil, Commentf(test.ErrErrorFmt, testName)) | ||
} else { | ||
c.Assert(err, IsNil, Commentf(test.ErrErrorFmt, testName)) | ||
} | ||
}() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.