mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2024-11-22 04:55:33 +03:00
Pull request: all: support setgid, setuid on unix
Updates #2763. Squashed commit of the following: commit bd2077c6569b53ae341a58aa73de6063d7037e8e Author: Ainar Garipov <A.Garipov@AdGuard.COM> Date: Fri Jun 4 16:25:17 2021 +0300 all: move rlimit_nofile, imp docs commit ba95d4ab7c722bf83300d626a598aface37539ad Author: Ainar Garipov <A.Garipov@AdGuard.COM> Date: Fri Jun 4 15:12:23 2021 +0300 all: support setgid, setuid on unix
This commit is contained in:
parent
3b87478470
commit
48c44c29ab
14 changed files with 283 additions and 31 deletions
|
@ -15,6 +15,7 @@ and this project adheres to
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- The ability to change group and user ID on startup on Unix ([#2763]).
|
||||||
- Experimental OpenBSD support for AMD64 and 64-bit ARM CPUs ([#2439]).
|
- Experimental OpenBSD support for AMD64 and 64-bit ARM CPUs ([#2439]).
|
||||||
- Support for custom port in DNS-over-HTTPS profiles for Apple's devices
|
- Support for custom port in DNS-over-HTTPS profiles for Apple's devices
|
||||||
([#3172]).
|
([#3172]).
|
||||||
|
@ -31,6 +32,8 @@ and this project adheres to
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- The setting `rlimit_nofile` is now in the `os` block of the configuration
|
||||||
|
file, together with the new `group` and `user` settings ([#2763]).
|
||||||
- Permissions on filter files are now `0o644` instead of `0o600` ([#3198]).
|
- Permissions on filter files are now `0o644` instead of `0o600` ([#3198]).
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
|
@ -56,6 +59,7 @@ released by then.
|
||||||
[#2439]: https://github.com/AdguardTeam/AdGuardHome/issues/2439
|
[#2439]: https://github.com/AdguardTeam/AdGuardHome/issues/2439
|
||||||
[#2441]: https://github.com/AdguardTeam/AdGuardHome/issues/2441
|
[#2441]: https://github.com/AdguardTeam/AdGuardHome/issues/2441
|
||||||
[#2443]: https://github.com/AdguardTeam/AdGuardHome/issues/2443
|
[#2443]: https://github.com/AdguardTeam/AdGuardHome/issues/2443
|
||||||
|
[#2763]: https://github.com/AdguardTeam/AdGuardHome/issues/2763
|
||||||
[#3136]: https://github.com/AdguardTeam/AdGuardHome/issues/3136
|
[#3136]: https://github.com/AdguardTeam/AdGuardHome/issues/3136
|
||||||
[#3172]: https://github.com/AdguardTeam/AdGuardHome/issues/3172
|
[#3172]: https://github.com/AdguardTeam/AdGuardHome/issues/3172
|
||||||
[#3184]: https://github.com/AdguardTeam/AdGuardHome/issues/3184
|
[#3184]: https://github.com/AdguardTeam/AdGuardHome/issues/3184
|
||||||
|
|
|
@ -126,6 +126,9 @@ on GitHub and most other Markdown renderers. -->
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Don't rely only on file names for build tags to work. Always add build tags
|
||||||
|
as well.
|
||||||
|
|
||||||
* Don't use `fmt.Sprintf` where a more structured approach to string
|
* Don't use `fmt.Sprintf` where a more structured approach to string
|
||||||
conversion could be used. For example, `net.JoinHostPort` or
|
conversion could be used. For example, `net.JoinHostPort` or
|
||||||
`url.(*URL).String`.
|
`url.(*URL).String`.
|
||||||
|
|
|
@ -4,17 +4,30 @@ package aghos
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrUnsupported is returned when the functionality is unsupported on the
|
// UnsupportedError is returned by functions and methods when a particular
|
||||||
// current operating system.
|
// operation Op cannot be performed on the current OS.
|
||||||
//
|
type UnsupportedError struct {
|
||||||
// TODO(a.garipov): Make a structured error and use it everywhere instead of
|
Op string
|
||||||
// a bunch of fmt.Errorf and all that.
|
OS string
|
||||||
const ErrUnsupported errors.Error = "unsupported"
|
}
|
||||||
|
|
||||||
|
// Error implements the error interface for *UnsupportedError.
|
||||||
|
func (err *UnsupportedError) Error() (msg string) {
|
||||||
|
return fmt.Sprintf("%s is unsupported on %s", err.Op, err.OS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsupported is a helper that returns an *UnsupportedError with the Op field
|
||||||
|
// set to op and the OS field set to the current OS.
|
||||||
|
func Unsupported(op string) (err error) {
|
||||||
|
return &UnsupportedError{
|
||||||
|
Op: op,
|
||||||
|
OS: runtime.GOOS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CanBindPrivilegedPorts checks if current process can bind to privileged
|
// CanBindPrivilegedPorts checks if current process can bind to privileged
|
||||||
// ports.
|
// ports.
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
package aghos
|
package aghos
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
|
@ -16,7 +15,7 @@ func canBindPrivilegedPorts() (can bool, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setRlimit(val uint64) (err error) {
|
func setRlimit(val uint64) (err error) {
|
||||||
return ErrUnsupported
|
return Unsupported("setrlimit")
|
||||||
}
|
}
|
||||||
|
|
||||||
func haveAdminRights() (bool, error) {
|
func haveAdminRights() (bool, error) {
|
||||||
|
@ -41,7 +40,7 @@ func haveAdminRights() (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendProcessSignal(pid int, sig syscall.Signal) error {
|
func sendProcessSignal(pid int, sig syscall.Signal) error {
|
||||||
return fmt.Errorf("not supported on Windows")
|
return Unsupported("kill")
|
||||||
}
|
}
|
||||||
|
|
||||||
func isOpenWrt() (ok bool) {
|
func isOpenWrt() (ok bool) {
|
||||||
|
|
11
internal/aghos/user.go
Normal file
11
internal/aghos/user.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package aghos
|
||||||
|
|
||||||
|
// SetGroup sets the effective group ID of the calling process.
|
||||||
|
func SetGroup(groupName string) (err error) {
|
||||||
|
return setGroup(groupName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUser sets the effective user ID of the calling process.
|
||||||
|
func SetUser(userName string) (err error) {
|
||||||
|
return setUser(userName)
|
||||||
|
}
|
50
internal/aghos/user_unix.go
Normal file
50
internal/aghos/user_unix.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
// +build darwin freebsd linux netbsd openbsd
|
||||||
|
|
||||||
|
//go:build darwin || freebsd || linux || netbsd || openbsd
|
||||||
|
|
||||||
|
package aghos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os/user"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setGroup(groupName string) (err error) {
|
||||||
|
g, err := user.LookupGroup(groupName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("looking up group: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gid, err := strconv.Atoi(g.Gid)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing gid: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = syscall.Setgid(gid)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("setting gid: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUser(userName string) (err error) {
|
||||||
|
u, err := user.Lookup(userName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("looking up user: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uid, err := strconv.Atoi(u.Uid)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing uid: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = syscall.Setuid(uid)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("setting uid: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
16
internal/aghos/user_windows.go
Normal file
16
internal/aghos/user_windows.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package aghos
|
||||||
|
|
||||||
|
// TODO(a.garipov): Think of a way to implement these. Perhaps by using
|
||||||
|
// syscall.CreateProcessAsUser or something from the golang.org/x/sys module.
|
||||||
|
|
||||||
|
func setGroup(_ string) (err error) {
|
||||||
|
return Unsupported("setgid")
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUser(_ string) (err error) {
|
||||||
|
return Unsupported("setuid")
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||||
|
@ -41,7 +42,7 @@ func CheckIfOtherDHCPServersPresentV4(ifaceName string) (ok bool, err error) {
|
||||||
// TODO(a.garipov): Find out what this is about. Perhaps this
|
// TODO(a.garipov): Find out what this is about. Perhaps this
|
||||||
// information is outdated or at least incomplete.
|
// information is outdated or at least incomplete.
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
return false, fmt.Errorf("can't find DHCP server: not supported on macOS")
|
return false, aghos.Unsupported("CheckIfOtherDHCPServersPresentV4")
|
||||||
}
|
}
|
||||||
|
|
||||||
srcIP := ifaceIPNet[0]
|
srcIP := ifaceIPNet[0]
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
package dhcpd
|
package dhcpd
|
||||||
|
|
||||||
import "fmt"
|
import "github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||||
|
|
||||||
func CheckIfOtherDHCPServersPresentV4(ifaceName string) (bool, error) {
|
func CheckIfOtherDHCPServersPresentV4(ifaceName string) (bool, error) {
|
||||||
return false, fmt.Errorf("not supported")
|
return false, aghos.Unsupported("CheckIfOtherDHCPServersPresentV4")
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckIfOtherDHCPServersPresentV6(ifaceName string) (bool, error) {
|
func CheckIfOtherDHCPServersPresentV6(ifaceName string) (bool, error) {
|
||||||
return false, fmt.Errorf("not supported")
|
return false, aghos.Unsupported("CheckIfOtherDHCPServersPresentV6")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
package dhcpd
|
package dhcpd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||||
"golang.org/x/net/ipv4"
|
"golang.org/x/net/ipv4"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create a socket for receiving broadcast packets
|
// Create a socket for receiving broadcast packets
|
||||||
func newBroadcastPacketConn(bindAddr net.IP, port int, ifname string) (*ipv4.PacketConn, error) {
|
func newBroadcastPacketConn(_ net.IP, _ int, _ string) (*ipv4.PacketConn, error) {
|
||||||
return nil, errors.Error("newBroadcastPacketConn(): not supported on Windows")
|
return nil, aghos.Unsupported("newBroadcastPacketConn")
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,19 @@ type logSettings struct {
|
||||||
Verbose bool `yaml:"verbose"` // If true, verbose logging is enabled
|
Verbose bool `yaml:"verbose"` // If true, verbose logging is enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// osConfig contains OS-related configuration.
|
||||||
|
type osConfig struct {
|
||||||
|
// Group is the name of the group which AdGuard Home must switch to on
|
||||||
|
// startup. Empty string means no switching.
|
||||||
|
Group string `yaml:"group"`
|
||||||
|
// User is the name of the user which AdGuard Home must switch to on
|
||||||
|
// startup. Empty string means no switching.
|
||||||
|
User string `yaml:"user"`
|
||||||
|
// RlimitNoFile is the maximum number of opened fd's per process. Zero
|
||||||
|
// means use the default value.
|
||||||
|
RlimitNoFile uint64 `yaml:"rlimit_nofile"`
|
||||||
|
}
|
||||||
|
|
||||||
// configuration is loaded from YAML
|
// configuration is loaded from YAML
|
||||||
// field ordering is important -- yaml fields will mirror ordering from here
|
// field ordering is important -- yaml fields will mirror ordering from here
|
||||||
type configuration struct {
|
type configuration struct {
|
||||||
|
@ -54,7 +67,6 @@ type configuration struct {
|
||||||
AuthBlockMin uint `yaml:"block_auth_min"`
|
AuthBlockMin uint `yaml:"block_auth_min"`
|
||||||
ProxyURL string `yaml:"http_proxy"` // Proxy address for our HTTP client
|
ProxyURL string `yaml:"http_proxy"` // Proxy address for our HTTP client
|
||||||
Language string `yaml:"language"` // two-letter ISO 639-1 language code
|
Language string `yaml:"language"` // two-letter ISO 639-1 language code
|
||||||
RlimitNoFile uint64 `yaml:"rlimit_nofile"` // Maximum number of opened fd's per process (0: default)
|
|
||||||
DebugPProf bool `yaml:"debug_pprof"` // Enable pprof HTTP server on port 6060
|
DebugPProf bool `yaml:"debug_pprof"` // Enable pprof HTTP server on port 6060
|
||||||
|
|
||||||
// TTL for a web session (in hours)
|
// TTL for a web session (in hours)
|
||||||
|
@ -75,6 +87,8 @@ type configuration struct {
|
||||||
|
|
||||||
logSettings `yaml:",inline"`
|
logSettings `yaml:",inline"`
|
||||||
|
|
||||||
|
OSConfig *osConfig `yaml:"os"`
|
||||||
|
|
||||||
sync.RWMutex `yaml:"-"`
|
sync.RWMutex `yaml:"-"`
|
||||||
|
|
||||||
SchemaVersion int `yaml:"schema_version"` // keeping last so that users will be less tempted to change it -- used when upgrading between versions
|
SchemaVersion int `yaml:"schema_version"` // keeping last so that users will be less tempted to change it -- used when upgrading between versions
|
||||||
|
@ -184,6 +198,7 @@ var config = configuration{
|
||||||
LogMaxSize: 100,
|
LogMaxSize: 100,
|
||||||
LogMaxAge: 3,
|
LogMaxAge: 3,
|
||||||
},
|
},
|
||||||
|
OSConfig: &osConfig{},
|
||||||
SchemaVersion: currentSchemaVersion,
|
SchemaVersion: currentSchemaVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -119,7 +119,7 @@ func Main(clientBuildFS fs.FS) {
|
||||||
// support OpenBSD currently. Either patch it to do so or make
|
// support OpenBSD currently. Either patch it to do so or make
|
||||||
// our own implementation of the service.System interface.
|
// our own implementation of the service.System interface.
|
||||||
if runtime.GOOS == "openbsd" {
|
if runtime.GOOS == "openbsd" {
|
||||||
log.Fatal("service actions are not supported on openbsd")
|
log.Fatal("service actions are not supported on openbsd, see issue 3226")
|
||||||
}
|
}
|
||||||
|
|
||||||
handleServiceControlAction(args, clientBuildFS)
|
handleServiceControlAction(args, clientBuildFS)
|
||||||
|
@ -183,6 +183,59 @@ func setupContext(args options) {
|
||||||
Context.mux = http.NewServeMux()
|
Context.mux = http.NewServeMux()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// logIfUnsupported logs a formatted warning if the error is one of the
|
||||||
|
// unsupported errors and returns nil. If err is nil, logIfUnsupported returns
|
||||||
|
// nil. Otherise, it returns err.
|
||||||
|
func logIfUnsupported(msg string, err error) (outErr error) {
|
||||||
|
if unsupErr := (&aghos.UnsupportedError{}); errors.As(err, &unsupErr) {
|
||||||
|
log.Debug(msg, err)
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// configureOS sets the OS-related configuration.
|
||||||
|
func configureOS(conf *configuration) (err error) {
|
||||||
|
osConf := conf.OSConfig
|
||||||
|
if osConf == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if osConf.Group != "" {
|
||||||
|
err = aghos.SetGroup(osConf.Group)
|
||||||
|
err = logIfUnsupported("warning: setting group", err)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("setting group: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("group set to %s", osConf.Group)
|
||||||
|
}
|
||||||
|
|
||||||
|
if osConf.User != "" {
|
||||||
|
err = aghos.SetUser(osConf.User)
|
||||||
|
err = logIfUnsupported("warning: setting user", err)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("setting user: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("user set to %s", osConf.User)
|
||||||
|
}
|
||||||
|
|
||||||
|
if osConf.RlimitNoFile != 0 {
|
||||||
|
err = aghos.SetRlimit(osConf.RlimitNoFile)
|
||||||
|
err = logIfUnsupported("warning: setting rlimit", err)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("setting rlimit: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("rlimit_nofile set to %d", osConf.RlimitNoFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func setupConfig(args options) (err error) {
|
func setupConfig(args options) (err error) {
|
||||||
config.DHCP.WorkDir = Context.workDir
|
config.DHCP.WorkDir = Context.workDir
|
||||||
config.DHCP.HTTPRegister = httpRegister
|
config.DHCP.HTTPRegister = httpRegister
|
||||||
|
@ -216,13 +269,6 @@ func setupConfig(args options) (err error) {
|
||||||
Context.clients.Init(config.Clients, Context.dhcpServer, Context.etcHosts)
|
Context.clients.Init(config.Clients, Context.dhcpServer, Context.etcHosts)
|
||||||
config.Clients = nil
|
config.Clients = nil
|
||||||
|
|
||||||
if config.RlimitNoFile != 0 {
|
|
||||||
err = aghos.SetRlimit(config.RlimitNoFile)
|
|
||||||
if err != nil && !errors.Is(err, aghos.ErrUnsupported) {
|
|
||||||
return fmt.Errorf("setting rlimit: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// override bind host/port from the console
|
// override bind host/port from the console
|
||||||
if args.bindHost != nil {
|
if args.bindHost != nil {
|
||||||
config.BindHost = args.bindHost
|
config.BindHost = args.bindHost
|
||||||
|
@ -309,6 +355,9 @@ func run(args options, clientBuildFS fs.FS) {
|
||||||
|
|
||||||
setupContext(args)
|
setupContext(args)
|
||||||
|
|
||||||
|
err = configureOS(&config)
|
||||||
|
fatalOnError(err)
|
||||||
|
|
||||||
// clients package uses filtering package's static data (filtering.BlockedSvcKnown()),
|
// clients package uses filtering package's static data (filtering.BlockedSvcKnown()),
|
||||||
// so we have to initialize filtering's static data first,
|
// so we have to initialize filtering's static data first,
|
||||||
// but also avoid relying on automatic Go init() function
|
// but also avoid relying on automatic Go init() function
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// currentSchemaVersion is the current schema version.
|
// currentSchemaVersion is the current schema version.
|
||||||
const currentSchemaVersion = 10
|
const currentSchemaVersion = 11
|
||||||
|
|
||||||
// These aliases are provided for convenience.
|
// These aliases are provided for convenience.
|
||||||
type (
|
type (
|
||||||
|
@ -81,6 +81,7 @@ func upgradeConfigSchema(oldVersion int, diskConf yobj) (err error) {
|
||||||
upgradeSchema7to8,
|
upgradeSchema7to8,
|
||||||
upgradeSchema8to9,
|
upgradeSchema8to9,
|
||||||
upgradeSchema9to10,
|
upgradeSchema9to10,
|
||||||
|
upgradeSchema10to11,
|
||||||
}
|
}
|
||||||
|
|
||||||
n := 0
|
n := 0
|
||||||
|
@ -611,6 +612,41 @@ func upgradeSchema9to10(diskConf yobj) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// upgradeSchema10to11 performs the following changes:
|
||||||
|
//
|
||||||
|
// # BEFORE:
|
||||||
|
// 'rlimit_nofile': 42
|
||||||
|
//
|
||||||
|
// # AFTER:
|
||||||
|
// 'os':
|
||||||
|
// 'group': ''
|
||||||
|
// 'rlimit_nofile': 42
|
||||||
|
// 'user': ''
|
||||||
|
//
|
||||||
|
func upgradeSchema10to11(diskConf yobj) (err error) {
|
||||||
|
log.Printf("Upgrade yaml: 10 to 11")
|
||||||
|
|
||||||
|
diskConf["schema_version"] = 11
|
||||||
|
|
||||||
|
rlimit := 0
|
||||||
|
rlimitVal, ok := diskConf["rlimit_nofile"]
|
||||||
|
if ok {
|
||||||
|
rlimit, ok = rlimitVal.(int)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unexpected type of rlimit_nofile: %T", rlimitVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(diskConf, "rlimit_nofile")
|
||||||
|
diskConf["os"] = yobj{
|
||||||
|
"group": "",
|
||||||
|
"rlimit_nofile": rlimit,
|
||||||
|
"user": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(a.garipov): Replace with log.Output when we port it to our logging
|
// TODO(a.garipov): Replace with log.Output when we port it to our logging
|
||||||
// package.
|
// package.
|
||||||
func funcName() string {
|
func funcName() string {
|
||||||
|
|
|
@ -368,3 +368,50 @@ func TestUpgradeSchema9to10(t *testing.T) {
|
||||||
assert.Equal(t, "unexpected type of dns: int", err.Error())
|
assert.Equal(t, "unexpected type of dns: int", err.Error())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpgradeSchema10to11(t *testing.T) {
|
||||||
|
check := func(t *testing.T, conf yobj) {
|
||||||
|
rlimit, _ := conf["rlimit_nofile"].(int)
|
||||||
|
|
||||||
|
err := upgradeSchema10to11(conf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, conf["schema_version"], 11)
|
||||||
|
|
||||||
|
_, ok := conf["rlimit_nofile"]
|
||||||
|
assert.False(t, ok)
|
||||||
|
|
||||||
|
osVal, ok := conf["os"]
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
newOSConf, ok := osVal.(yobj)
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
_, ok = newOSConf["group"]
|
||||||
|
assert.True(t, ok)
|
||||||
|
|
||||||
|
_, ok = newOSConf["user"]
|
||||||
|
assert.True(t, ok)
|
||||||
|
|
||||||
|
rlimitVal, ok := newOSConf["rlimit_nofile"].(int)
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
assert.Equal(t, rlimit, rlimitVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
const rlimit = 42
|
||||||
|
t.Run("with_rlimit", func(t *testing.T) {
|
||||||
|
conf := yobj{
|
||||||
|
"rlimit_nofile": rlimit,
|
||||||
|
"schema_version": 10,
|
||||||
|
}
|
||||||
|
check(t, conf)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("without_rlimit", func(t *testing.T) {
|
||||||
|
conf := yobj{
|
||||||
|
"schema_version": 10,
|
||||||
|
}
|
||||||
|
check(t, conf)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue