Initial WIP logic to handle loading configuration from the disk using viper

This commit is contained in:
Dane Everitt 2021-01-12 21:14:57 -08:00
parent d45a159456
commit 9480ccdbba
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
9 changed files with 213 additions and 287 deletions

1
.gitignore vendored
View File

@ -22,6 +22,7 @@
# ignore configuration file
/config.yml
/config*.yml
# Ignore Vagrant stuff
/.vagrant

View File

@ -1,61 +0,0 @@
package cmd
import (
"os"
"path/filepath"
"github.com/pterodactyl/wings/config"
)
// We've gone through a couple of iterations of where the configuration is stored. This
// helpful little function will look through the three areas it might have ended up, and
// return it.
//
// We only run this if the configuration flag for the instance is not actually passed in
// via the command line. Once found, the configuration is moved into the expected default
// location. Only errors are returned from this function, you can safely assume that after
// running this the configuration can be found in the correct default location.
func RelocateConfiguration() error {
var match string
check := []string{
config.DefaultLocation,
"/var/lib/pterodactyl/config.yml",
"/etc/wings/config.yml",
}
// Loop over all of the configuration paths, and return which one we found, if
// any.
for _, p := range check {
if s, err := os.Stat(p); err != nil {
if !os.IsNotExist(err) {
return err
}
} else if !s.IsDir() {
match = p
break
}
}
// Just return a generic not exist error at this point if we didn't have a match, this
// will allow the caller to handle displaying a more friendly error to the user. If we
// did match in the default location, go ahead and return successfully.
if match == "" {
return os.ErrNotExist
} else if match == config.DefaultLocation {
return nil
}
// The rest of this function simply creates the new default location and moves the
// old configuration file over to the new location, then sets the permissions on the
// file correctly so that only the user running this process can read it.
p, _ := filepath.Split(config.DefaultLocation)
if err := os.MkdirAll(p, 0755); err != nil {
return err
}
if err := os.Rename(match, config.DefaultLocation); err != nil {
return err
}
return os.Chmod(config.DefaultLocation, 0600)
}

View File

@ -26,12 +26,13 @@ import (
"github.com/pterodactyl/wings/sftp"
"github.com/pterodactyl/wings/system"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"golang.org/x/crypto/acme"
"golang.org/x/crypto/acme/autocert"
)
var (
configPath = config.DefaultLocation
configPath = ""
debug = false
)
@ -64,7 +65,9 @@ func Execute() {
}
func init() {
rootCommand.PersistentFlags().StringVar(&configPath, "config", config.DefaultLocation, "set the location for the configuration file")
cobra.OnInitialize(initConfig, initLogging)
rootCommand.PersistentFlags().StringVar(&configPath, "config", "", "set the location for the configuration file")
rootCommand.PersistentFlags().BoolVar(&debug, "debug", false, "pass in order to run wings in debug mode")
// Flags specifically used when running the API.
@ -119,17 +122,6 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
defer profile.Start(profile.BlockProfile).Stop()
}
// Only attempt configuration file relocation if a custom location has not
// been specified in the command startup.
if configPath == config.DefaultLocation {
if err := RelocateConfiguration(); err != nil {
if errors.Is(err, os.ErrNotExist) {
exitWithConfigurationNotice()
}
panic(err)
}
}
c, err := readConfiguration()
if err != nil {
panic(err)
@ -140,14 +132,8 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
}
printLogo()
if err := configureLogging(c.System.LogDirectory, c.Debug); err != nil {
panic(err)
}
log.WithField("path", c.GetPath()).Info("loading configuration from path")
if c.Debug {
log.WithField("path", viper.ConfigFileUsed()).Info("loading configuration from file")
log.Debug("running in debug mode")
}
if ok, _ := cmd.Flags().GetBool("ignore-certificate-errors"); ok {
log.Warn("running with --ignore-certificate-errors: TLS certificate host chains and name will not be verified")
@ -158,47 +144,42 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
config.Set(c)
config.SetDebugViaFlag(debug)
if err := c.System.ConfigureTimezone(); err != nil {
if err := config.ConfigureTimezone(); err != nil {
log.WithField("error", err).Fatal("failed to detect system timezone or use supplied configuration value")
return
}
log.WithField("timezone", c.System.Timezone).Info("configured wings with system timezone")
if err := c.System.ConfigureDirectories(); err != nil {
if err := config.ConfigureDirectories(); err != nil {
log.WithField("error", err).Fatal("failed to configure system directories for pterodactyl")
return
}
if err := c.System.EnableLogRotation(); err != nil {
if err := config.EnableLogRotation(); err != nil {
log.WithField("error", err).Fatal("failed to configure log rotation on the system")
return
}
log.WithField("username", c.System.Username).Info("checking for pterodactyl system user")
if su, err := c.EnsurePterodactylUser(); err != nil {
if err := config.EnsurePterodactylUser(); err != nil {
log.WithField("error", err).Fatal("failed to create pterodactyl system user")
return
} else {
log.WithFields(log.Fields{
"username": su.Username,
"uid": su.Uid,
"gid": su.Gid,
}).Info("configured system user successfully")
}
log.WithFields(log.Fields{
"username": viper.GetString("system.username"),
"uid": viper.GetInt("system.user.uid"),
"gid": viper.GetInt("system.user.gid"),
}).Info("configured system user successfully")
if err := server.LoadDirectory(); err != nil {
log.WithField("error", err).Fatal("failed to load server configurations")
return
}
if err := environment.ConfigureDocker(&c.Docker); err != nil {
if err := environment.ConfigureDocker(cmd.Context()); err != nil {
log.WithField("error", err).Fatal("failed to configure docker environment")
return
}
if err := c.WriteToDisk(); err != nil {
if err := viper.WriteConfig(); err != nil {
log.WithField("error", err).Error("failed to save configuration to disk")
}
@ -379,28 +360,44 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
}
}
// Configures the global logger for Zap so that we can call it from any location
// in the code without having to pass around a logger instance.
func configureLogging(logDir string, debug bool) error {
if err := os.MkdirAll(path.Join(logDir, "/install"), 0700); err != nil {
return err
func initConfig() {
if configPath != "" {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath("/etc/pterodactyl")
viper.AddConfigPath("$HOME/.pterodactyl")
viper.AddConfigPath(".")
} else {
viper.SetConfigFile(configPath)
}
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(*viper.ConfigFileNotFoundError); ok {
exitWithConfigurationNotice()
}
log2.Fatalf("cmd/root: failed to read configuration: %s", err)
}
}
p := filepath.Join(logDir, "/wings.log")
// Configures the global logger for Zap so that we can call it from any location
// in the code without having to pass around a logger instance.
func initLogging() {
dir := viper.GetString("system.log_directory")
if err := os.MkdirAll(path.Join(dir, "/install"), 0700); err != nil {
log2.Fatalf("cmd/root: failed to create install directory path: %s", err)
}
p := filepath.Join(dir, "/wings.log")
w, err := logrotate.NewFile(p)
if err != nil {
return err
log2.Fatalf("cmd/root: failed to create wings log: %s", err)
}
log.SetLevel(log.InfoLevel)
if debug {
if viper.GetBool("debug") {
log.SetLevel(log.DebugLevel)
}
log.SetHandler(multi.New(cli.Default, cli.New(w.File, false)))
log.WithField("path", p).Info("writing log files to disk")
return nil
}
// Prints the wings logo, nothing special here!
@ -429,11 +426,8 @@ func exitWithConfigurationNotice() {
[_red_][white][bold]Error: Configuration File Not Found[reset]
Wings was not able to locate your configuration file, and therefore is not
able to complete its boot process.
Please ensure you have copied your instance configuration file into
the default location, or have provided the --config flag to use a
custom location.
able to complete its boot process. Please ensure you have copied your instance
configuration file into the default location below.
Default Location: /etc/pterodactyl/config.yml

View File

@ -6,7 +6,6 @@ import (
"os"
"os/exec"
"os/user"
"strconv"
"strings"
"sync"
@ -14,6 +13,8 @@ import (
"github.com/cobaugh/osrelease"
"github.com/creasty/defaults"
"github.com/gbrlsnchs/jwt/v3"
"github.com/pterodactyl/wings/system"
"github.com/spf13/viper"
"gopkg.in/yaml.v2"
)
@ -223,98 +224,64 @@ func (c *Configuration) GetPath() string {
return c.path
}
// Ensures that the Pterodactyl core user exists on the system. This user will be the
// owner of all data in the root data directory and is used as the user within containers.
// EnsurePterodactylUser ensures that the Pterodactyl core user exists on the
// system. This user will be the owner of all data in the root data directory
// and is used as the user within containers.
//
// If files are not owned by this user there will be issues with permissions on Docker
// mount points.
func (c *Configuration) EnsurePterodactylUser() (*user.User, error) {
// If files are not owned by this user there will be issues with permissions on
// Docker mount points.
func EnsurePterodactylUser() error {
sysName, err := getSystemName()
if err != nil {
return nil, err
return err
}
// Our way of detecting if wings is running inside of Docker.
if sysName == "busybox" {
uid := os.Getenv("WINGS_UID")
if uid == "" {
uid = "988"
viper.Set("system.username", system.FirstNotEmpty(os.Getenv("WINGS_USERNAME"), "pterodactyl"))
viper.Set("system.user.uid", system.MustInt(system.FirstNotEmpty(os.Getenv("WINGS_UID"), "988")))
viper.Set("system.user.gid", system.MustInt(system.FirstNotEmpty(os.Getenv("WINGS_GID"), "988")))
return nil
}
gid := os.Getenv("WINGS_GID")
if gid == "" {
gid = "988"
}
username := os.Getenv("WINGS_USERNAME")
if username == "" {
username = "pterodactyl"
}
u := &user.User{
Uid: uid,
Gid: gid,
Username: username,
}
return u, c.setSystemUser(u)
}
u, err := user.Lookup(c.System.Username)
username := viper.GetString("system.username")
u, err := user.Lookup(username)
// If an error is returned but it isn't the unknown user error just abort
// the process entirely. If we did find a user, return it immediately.
if err == nil {
return u, c.setSystemUser(u)
} else if _, ok := err.(user.UnknownUserError); !ok {
return nil, err
if err != nil {
if _, ok := err.(user.UnknownUserError); !ok {
return err
}
} else {
viper.Set("system.user.uid", system.MustInt(u.Uid))
viper.Set("system.user.gid", system.MustInt(u.Gid))
return nil
}
command := fmt.Sprintf("useradd --system --no-create-home --shell /usr/sbin/nologin %s", c.System.Username)
// Alpine Linux is the only OS we currently support that doesn't work with the useradd command, so
// in those cases we just modify the command a bit to work as expected.
command := fmt.Sprintf("useradd --system --no-create-home --shell /usr/sbin/nologin %s", username)
// Alpine Linux is the only OS we currently support that doesn't work with the useradd
// command, so in those cases we just modify the command a bit to work as expected.
if strings.HasPrefix(sysName, "alpine") {
command = fmt.Sprintf("adduser -S -D -H -G %[1]s -s /sbin/nologin %[1]s", c.System.Username)
command = fmt.Sprintf("adduser -S -D -H -G %[1]s -s /sbin/nologin %[1]s", username)
// We have to create the group first on Alpine, so do that here before continuing on
// to the user creation process.
if _, err := exec.Command("addgroup", "-S", c.System.Username).Output(); err != nil {
return nil, err
if _, err := exec.Command("addgroup", "-S", username).Output(); err != nil {
return err
}
}
split := strings.Split(command, " ")
if _, err := exec.Command(split[0], split[1:]...).Output(); err != nil {
return nil, err
}
if u, err := user.Lookup(c.System.Username); err != nil {
return nil, err
} else {
return u, c.setSystemUser(u)
}
}
// Set the system user into the configuration and then write it to the disk so that
// it is persisted on boot.
func (c *Configuration) setSystemUser(u *user.User) error {
uid, err := strconv.Atoi(u.Uid)
if err != nil {
return err
}
gid, err := strconv.Atoi(u.Gid)
u, err = user.Lookup(username)
if err != nil {
return err
}
c.Lock()
c.System.Username = u.Username
c.System.User.Uid = uid
c.System.User.Gid = gid
c.Unlock()
return c.WriteToDisk()
viper.Set("system.user.uid", system.MustInt(u.Uid))
viper.Set("system.user.gid", system.MustInt(u.Gid))
return nil
}
// Writes the configuration to the disk as a blocking operation by obtaining an exclusive

View File

@ -14,6 +14,7 @@ import (
"emperror.dev/errors"
"github.com/apex/log"
"github.com/spf13/viper"
)
// Defines basic system configuration settings.
@ -116,11 +117,13 @@ type Transfers struct {
DownloadLimit int `default:"0" yaml:"download_limit"`
}
// Ensures that all of the system directories exist on the system. These directories are
// created so that only the owner can read the data, and no other users.
func (sc *SystemConfiguration) ConfigureDirectories() error {
log.WithField("path", sc.RootDirectory).Debug("ensuring root data directory exists")
if err := os.MkdirAll(sc.RootDirectory, 0700); err != nil {
// ConfigureDirectories ensures that all of the system directories exist on the
// system. These directories are created so that only the owner can read the data,
// and no other users.
func ConfigureDirectories() error {
root := viper.GetString("system.root_directory")
log.WithField("path", root).Debug("ensuring root data directory exists")
if err := os.MkdirAll(root, 0700); err != nil {
return err
}
@ -132,40 +135,42 @@ func (sc *SystemConfiguration) ConfigureDirectories() error {
// For the sake of automating away as much of this as possible, see if the data directory is a
// symlink, and if so resolve to its final real path, and then update the configuration to use
// that.
if d, err := filepath.EvalSymlinks(sc.Data); err != nil {
data := viper.GetString("system.data")
if d, err := filepath.EvalSymlinks(data); err != nil {
if !os.IsNotExist(err) {
return err
}
} else if d != sc.Data {
sc.Data = d
} else if d != data {
data = d
viper.Set("system.data", d)
}
log.WithField("path", sc.Data).Debug("ensuring server data directory exists")
if err := os.MkdirAll(sc.Data, 0700); err != nil {
log.WithField("path", data).Debug("ensuring server data directory exists")
if err := os.MkdirAll(data, 0700); err != nil {
return err
}
log.WithField("path", sc.ArchiveDirectory).Debug("ensuring archive data directory exists")
if err := os.MkdirAll(sc.ArchiveDirectory, 0700); err != nil {
log.WithField("path", viper.GetString("system.archive_directory")).Debug("ensuring archive data directory exists")
if err := os.MkdirAll(viper.GetString("system.archive_directory"), 0700); err != nil {
return err
}
log.WithField("path", sc.BackupDirectory).Debug("ensuring backup data directory exists")
if err := os.MkdirAll(sc.BackupDirectory, 0700); err != nil {
log.WithField("path", viper.GetString("system.backup_directory")).Debug("ensuring backup data directory exists")
if err := os.MkdirAll(viper.GetString("system.backup_directory"), 0700); err != nil {
return err
}
return nil
}
// Writes a logrotate file for wings to the system logrotate configuration directory if one
// exists and a logrotate file is not found. This allows us to basically automate away the log
// rotation for most installs, but also enable users to make modifications on their own.
func (sc *SystemConfiguration) EnableLogRotation() error {
// EnableLogRotation writes a logrotate file for wings to the system logrotate
// configuration directory if one exists and a logrotate file is not found. This
// allows us to basically automate away the log rotation for most installs, but
// also enable users to make modifications on their own.
func EnableLogRotation() error {
// Do nothing if not enabled.
if sc.EnableLogRotate == false {
if !viper.GetBool("system.enable_log_rotate") {
log.Info("skipping log rotate configuration, disabled in wings config file")
return nil
}
@ -174,14 +179,11 @@ func (sc *SystemConfiguration) EnableLogRotation() error {
} else if (err != nil && os.IsNotExist(err)) || !st.IsDir() {
return nil
}
if _, err := os.Stat("/etc/logrotate.d/wings"); err != nil && !os.IsNotExist(err) {
if _, err := os.Stat("/etc/logrotate.d/wings"); err == nil || !os.IsNotExist(err) {
return err
} else if err == nil {
return nil
}
log.Info("no log rotation configuration found, system is configured to support it, adding file now")
log.Info("no log rotation configuration found: adding file now")
// If we've gotten to this point it means the logrotate directory exists on the system
// but there is not a file for wings already. In that case, let us write a new file to
// it so files can be rotated easily.
@ -191,8 +193,14 @@ func (sc *SystemConfiguration) EnableLogRotation() error {
}
defer f.Close()
type logrotateConfig struct {
Directory string
UserID int
GroupID int
}
t, err := template.New("logrotate").Parse(`
{{.LogDirectory}}/wings.log {
{{.Directory}}/wings.log {
size 10M
compress
delaycompress
@ -200,17 +208,21 @@ func (sc *SystemConfiguration) EnableLogRotation() error {
maxage 7
missingok
notifempty
create 0640 {{.User.Uid}} {{.User.Gid}}
create 0640 {{.UserID}} {{.GroupID}}
postrotate
killall -SIGHUP wings
endscript
}`)
if err != nil {
return err
}
return errors.WithMessage(t.Execute(f, sc), "failed to write logrotate file to disk")
err = t.Execute(f, logrotateConfig{
Directory: viper.GetString("system.log_directory"),
UserID: viper.GetInt("system.user.uid"),
GroupID: viper.GetInt("system.user.gid"),
})
return errors.Wrap(err, "config: failed to write logrotate to disk")
}
// Returns the location of the JSON file that tracks server states.
@ -223,25 +235,28 @@ func (sc *SystemConfiguration) GetInstallLogPath() string {
return path.Join(sc.LogDirectory, "install/")
}
// Configures the timezone data for the configuration if it is currently missing. If
// a value has been set, this functionality will only run to validate that the timezone
// being used is valid.
func (sc *SystemConfiguration) ConfigureTimezone() error {
if sc.Timezone == "" {
if b, err := ioutil.ReadFile("/etc/timezone"); err != nil {
// ConfigureTimezone sets the timezone data for the configuration if it is
// currently missing. If a value has been set, this functionality will only run
// to validate that the timezone being used is valid.
func ConfigureTimezone() error {
tz := viper.GetString("system.timezone")
defer viper.Set("system.timezone", tz)
if tz == "" {
b, err := ioutil.ReadFile("/etc/timezone")
if err != nil {
if !os.IsNotExist(err) {
return errors.WithMessage(err, "failed to open /etc/timezone for automatic server timezone calibration")
return errors.WithMessage(err, "config: failed to open timezone file")
}
ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
tz = "UTC"
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
// Okay, file isn't found on this OS, we will try using timedatectl to handle this. If this
// command fails, exit, but if it returns a value use that. If no value is returned we will
// fall through to UTC to get Wings booted at least.
out, err := exec.CommandContext(ctx, "timedatectl").Output()
if err != nil {
log.WithField("error", err).Warn("failed to execute \"timedatectl\" to determine system timezone, falling back to UTC")
sc.Timezone = "UTC"
return nil
}
@ -249,20 +264,16 @@ func (sc *SystemConfiguration) ConfigureTimezone() error {
matches := r.FindSubmatch(out)
if len(matches) != 2 || string(matches[1]) == "" {
log.Warn("failed to parse timezone from \"timedatectl\" output, falling back to UTC")
sc.Timezone = "UTC"
return nil
}
sc.Timezone = string(matches[1])
tz = string(matches[1])
} else {
sc.Timezone = string(b)
tz = string(b)
}
}
sc.Timezone = regexp.MustCompile(`(?i)[^a-z_/]+`).ReplaceAllString(sc.Timezone, "")
tz = regexp.MustCompile(`(?i)[^a-z_/]+`).ReplaceAllString(tz, "")
_, err := time.LoadLocation(tz)
_, err := time.LoadLocation(sc.Timezone)
return errors.WithMessage(err, fmt.Sprintf("the supplied timezone %s is invalid", sc.Timezone))
return errors.WithMessage(err, fmt.Sprintf("the supplied timezone %s is invalid", tz))
}

View File

@ -6,112 +6,95 @@ import (
"sync"
"github.com/apex/log"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
"github.com/pterodactyl/wings/config"
"github.com/spf13/viper"
)
var _cmu sync.Mutex
var _conce sync.Once
var _client *client.Client
// Return a Docker client to be used throughout the codebase. Once a client has been created it
// will be returned for all subsequent calls to this function.
// DockerClient returns a Docker client to be used throughout the codebase. Once
// a client has been created it will be returned for all subsequent calls to this
// function.
func DockerClient() (*client.Client, error) {
_cmu.Lock()
defer _cmu.Unlock()
if _client != nil {
return _client, nil
}
_client, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
var err error
_conce.Do(func() {
_client, err = client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
})
return _client, err
}
// Configures the required network for the docker environment.
func ConfigureDocker(c *config.DockerConfiguration) error {
// ConfigureDocker configures the required network for the docker environment.
func ConfigureDocker(ctx context.Context) error {
// Ensure the required docker network exists on the system.
cli, err := DockerClient()
if err != nil {
return err
}
resource, err := cli.NetworkInspect(context.Background(), c.Network.Name, types.NetworkInspectOptions{})
if err != nil && client.IsErrNotFound(err) {
nw := viper.Sub("docker.network")
resource, err := cli.NetworkInspect(ctx, nw.GetString("name"), types.NetworkInspectOptions{})
if err != nil {
if client.IsErrNotFound(err) {
log.Info("creating missing pterodactyl0 interface, this could take a few seconds...")
return createDockerNetwork(cli, c)
} else if err != nil {
log.WithField("error", err).Fatal("failed to create required docker network for containers")
if err := createDockerNetwork(ctx, cli); err != nil {
return err
}
}
return err
} else {
nw.Set("driver", resource.Driver)
}
switch resource.Driver {
switch nw.GetString("driver") {
case "host":
c.Network.Interface = "127.0.0.1"
c.Network.ISPN = false
return nil
nw.Set("interface", "127.0.0.1")
nw.Set("ispn", false)
case "overlay":
fallthrough
case "weavemesh":
c.Network.Interface = ""
c.Network.ISPN = true
return nil
nw.Set("interface", "")
nw.Set("ispn", true)
default:
c.Network.ISPN = false
nw.Set("ispn", false)
}
return nil
}
// Creates a new network on the machine if one does not exist already.
func createDockerNetwork(cli *client.Client, c *config.DockerConfiguration) error {
_, err := cli.NetworkCreate(context.Background(), c.Network.Name, types.NetworkCreate{
Driver: c.Network.Driver,
func createDockerNetwork(ctx context.Context, cli *client.Client) error {
nw := viper.Sub("docker.network")
_, err := cli.NetworkCreate(ctx, nw.GetString("name"), types.NetworkCreate{
Driver: nw.GetString("driver"),
EnableIPv6: true,
Internal: c.Network.IsInternal,
Internal: nw.GetBool("is_internal"),
IPAM: &network.IPAM{
Config: []network.IPAMConfig{
{
Subnet: c.Network.Interfaces.V4.Subnet,
Gateway: c.Network.Interfaces.V4.Gateway,
Subnet: nw.GetString("interfaces.v4.subnet"),
Gateway: nw.GetString("interfaces.v4.gateway"),
},
{
Subnet: c.Network.Interfaces.V6.Subnet,
Gateway: c.Network.Interfaces.V6.Gateway,
Subnet: nw.GetString("interfaces.v6.subnet"),
Gateway: nw.GetString("interfaces.v6.gateway"),
},
},
},
Options: map[string]string{
"encryption": "false",
"com.docker.network.bridge.default_bridge": "false",
"com.docker.network.bridge.enable_icc": strconv.FormatBool(c.Network.EnableICC),
"com.docker.network.bridge.enable_icc": strconv.FormatBool(nw.GetBool("enable_icc")),
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "pterodactyl0",
"com.docker.network.driver.mtu": "1500",
},
})
if err != nil {
driver := nw.GetString("driver")
if driver != "host" && driver != "overlay" && driver != "weavemesh" {
nw.Set("interface", nw.GetString("interfaces.v4.gateway"))
}
return err
}
switch c.Network.Driver {
case "host":
c.Network.Interface = "127.0.0.1"
c.Network.ISPN = false
break
case "overlay":
case "weavemesh":
c.Network.Interface = ""
c.Network.ISPN = true
break
default:
c.Network.Interface = c.Network.Interfaces.V4.Gateway
c.Network.ISPN = false
break
}
return nil
}

1
go.mod
View File

@ -64,6 +64,7 @@ require (
github.com/sirupsen/logrus v1.7.0 // indirect
github.com/spf13/cobra v1.1.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.7.1
github.com/ugorji/go v1.2.2 // indirect
github.com/ulikunitz/xz v0.5.9 // indirect
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad

9
go.sum
View File

@ -285,6 +285,7 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
@ -401,6 +402,7 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
@ -454,6 +456,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
@ -550,11 +553,14 @@ github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4S
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
@ -562,6 +568,8 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
@ -575,6 +583,7 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tj/assert v0.0.0-20171129193455-018094318fb0 h1:Rw8kxzWo1mr6FSaYXjQELRe88y2KdfynXdnK72rdjtA=
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=

View File

@ -7,14 +7,35 @@ import (
"encoding/json"
"fmt"
"io"
"strconv"
"strings"
"sync"
"time"
"emperror.dev/errors"
)
var cr = []byte(" \r")
var crr = []byte("\r\n")
// FirstNotEmpty returns the first string passed in that is not an empty value.
func FirstNotEmpty(v ...string) string {
for _, val := range v {
if val != "" {
return val
}
}
return ""
}
func MustInt(v string) int {
i, err := strconv.Atoi(v)
if err != nil {
panic(errors.Wrap(err, "system/utils: could not parse int"))
}
return i
}
func ScanReader(r io.Reader, callback func(line string)) error {
br := bufio.NewReader(r)
// Avoid constantly re-allocating memory when we're flooding lines through this