Compare commits
27 Commits
v1.0.0-rc.
...
v1.0.0-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
481df3d543 | ||
|
|
cbf914e7a1 | ||
|
|
d742acf308 | ||
|
|
5f1d9ff151 | ||
|
|
1e633ae302 | ||
|
|
7d084e3049 | ||
|
|
c69a0bb107 | ||
|
|
9780cf902d | ||
|
|
f1343c1d77 | ||
|
|
3c662d5b07 | ||
|
|
7d8710824c | ||
|
|
711ee2258c | ||
|
|
60acee2df5 | ||
|
|
0dde54fc8f | ||
|
|
0e474c8b24 | ||
|
|
68ab705aac | ||
|
|
a7ca6b2e34 | ||
|
|
5f1ceeff90 | ||
|
|
c7e732d084 | ||
|
|
9eb795b1bb | ||
|
|
a1288565f0 | ||
|
|
f82c91afbe | ||
|
|
b35ac76720 | ||
|
|
9f27119044 | ||
|
|
9cd416611f | ||
|
|
459c370229 | ||
|
|
b3a2a76f25 |
43
README.md
43
README.md
@@ -1,16 +1,35 @@
|
|||||||
# Alpha Project
|
[](https://pterodactyl.io)
|
||||||
Please refrain from opening PRs or Issues at this time. This project is still under heavy development, and until we have a solid foundation and plan for how everything will connect, we will not be accepting PRs or feature suggestions.
|
|
||||||
|
|
||||||
# Pterodactyl wings [](https://travis-ci.org/pterodactyl/wings) [](https://www.codacy.com/app/schrej/wings/dashboard) [](https://www.codacy.com/app/schrej/wings/files)
|
[](https://pterodactyl.io/discord)
|
||||||
|
|
||||||
```
|
# Pterodactyl Wings
|
||||||
____
|
Wings is Pterodactyl's server control plane, built for the rapidly changing gaming industry and designed to be
|
||||||
__ Pterodactyl _____/___/_______ _______ ______
|
highly performant and secure. Wings provides an HTTP API allowing you to interface directly with running server
|
||||||
\_____\ \/\/ / / / __ / ___/
|
instances, fetch server logs, generate backups, and control all aspects of the server lifecycle.
|
||||||
\___\ / / / / /_/ /___ /
|
|
||||||
\___/\___/___/___/___/___ /______/
|
|
||||||
/_______/ alpha
|
|
||||||
```
|
|
||||||
|
|
||||||
A new generation of the Pterodactyl daemon, written in go.
|
In addition, Wings ships with a built-in SFTP server allowing your system to remain free of Pterodactyl specific
|
||||||
|
dependencies, and allowing users to authenticate with the same credentials they would normally use to access the Panel.
|
||||||
|
|
||||||
|
## Sponsors
|
||||||
|
I would like to extend my sincere thanks to the following sponsors for helping find Pterodactyl's developement.
|
||||||
|
[Interested in becoming a sponsor?](https://github.com/sponsors/DaneEveritt)
|
||||||
|
|
||||||
|
| Company | About |
|
||||||
|
| ------- | ----- |
|
||||||
|
| [**BloomVPS**](https://bloomvps.com) | BloomVPS offers dedicated core VPS and Minecraft hosting with Ryzen 9 processors. With owned-hardware, we offer truly unbeatable prices on high-performance hosting. |
|
||||||
|
| [**VersatileNode**](https://versatilenode.com/) | Looking to host a minecraft server, vps, or a website? VersatileNode is one of the most affordable hosting providers to provide quality yet cheap services with incredible support. |
|
||||||
|
| [**MineStrator**](https://minestrator.com/) | Looking for a French highend hosting company for you minecraft server? More than 14,000 members on our discord, trust us. |
|
||||||
|
| [**DedicatedMC**](https://dedicatedmc.io/) | DedicatedMC provides Raw Power hosting at affordable pricing, making sure to never compromise on your performance and giving you the best performance money can buy. |
|
||||||
|
| [**Skynode**](https://www.skynode.pro/) | Skynode provides blazing fast game servers along with a top-notch user experience. Whatever our clients are looking for, we're able to provide it! |
|
||||||
|
| [**XCORE-SERVER.de**](https://xcore-server.de/) | XCORE-SERVER.de offers High-End Servers for hosting and gaming since 2012. Fast, excellent and well-known for eSports Gaming. |
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
* [Panel Documentation](https://pterodactyl.io/panel/1.0/getting_started.html)
|
||||||
|
* [Wings Documentation](https://pterodactyl.io/wings/1.0/installing.html)
|
||||||
|
* [Community Guides](https://pterodactyl.io/community/about.html)
|
||||||
|
* Or, get additional help [via Discord](https://discord.gg/pterodactyl)
|
||||||
|
|
||||||
|
## Reporting Issues
|
||||||
|
Please use the [pterodactyl/panel](https://github.com/pterodactyl/panel) repository to report any issues or make
|
||||||
|
feature requests for Wings. In addition, the [security policy](https://github.com/pterodactyl/panel/security/policy) listed
|
||||||
|
within that repository also applies to Wings.
|
||||||
@@ -144,7 +144,7 @@ type RequestError struct {
|
|||||||
|
|
||||||
// Returns the error response in a string form that can be more easily consumed.
|
// Returns the error response in a string form that can be more easily consumed.
|
||||||
func (re *RequestError) Error() string {
|
func (re *RequestError) Error() string {
|
||||||
return fmt.Sprintf("%s: %s (HTTP/%s)", re.Code, re.Detail, re.Status)
|
return fmt.Sprintf("Error response from Panel: %s: %s (HTTP/%s)", re.Code, re.Detail, re.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (re *RequestError) String() string {
|
func (re *RequestError) String() string {
|
||||||
|
|||||||
@@ -2,11 +2,57 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"github.com/apex/log"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/pterodactyl/sftp-server"
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *PanelRequest) ValidateSftpCredentials(request sftp_server.AuthenticationRequest) (*sftp_server.AuthenticationResponse, error) {
|
type SftpAuthRequest struct {
|
||||||
|
User string `json:"username"`
|
||||||
|
Pass string `json:"password"`
|
||||||
|
IP string `json:"ip"`
|
||||||
|
SessionID []byte `json:"session_id"`
|
||||||
|
ClientVersion []byte `json:"client_version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SftpAuthResponse struct {
|
||||||
|
Server string `json:"server"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
Permissions []string `json:"permissions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type sftpInvalidCredentialsError struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ice sftpInvalidCredentialsError) Error() string {
|
||||||
|
return "the credentials provided were invalid"
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsInvalidCredentialsError(err error) bool {
|
||||||
|
_, ok := err.(*sftpInvalidCredentialsError)
|
||||||
|
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usernames all follow the same format, so don't even bother hitting the API if the username is not
|
||||||
|
// at least in the expected format. This is very basic protection against random bots finding the SFTP
|
||||||
|
// server and sending a flood of usernames.
|
||||||
|
var validUsernameRegexp = regexp.MustCompile(`^(?i)(.+)\.([a-z0-9]{8})$`)
|
||||||
|
|
||||||
|
func (r *PanelRequest) ValidateSftpCredentials(request SftpAuthRequest) (*SftpAuthResponse, error) {
|
||||||
|
// If the username doesn't meet the expected format that the Panel would even recognize just go ahead
|
||||||
|
// and bail out of the process here to avoid accidentially brute forcing the panel if a bot decides
|
||||||
|
// to connect to spam username attempts.
|
||||||
|
if !validUsernameRegexp.MatchString(request.User) {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"subsystem": "sftp",
|
||||||
|
"username": request.User,
|
||||||
|
"ip": request.IP,
|
||||||
|
}).Warn("failed to validate user credentials (invalid format)")
|
||||||
|
|
||||||
|
return nil, new(sftpInvalidCredentialsError)
|
||||||
|
}
|
||||||
|
|
||||||
b, err := json.Marshal(request)
|
b, err := json.Marshal(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -22,7 +68,7 @@ func (r *PanelRequest) ValidateSftpCredentials(request sftp_server.Authenticatio
|
|||||||
|
|
||||||
if r.HasError() {
|
if r.HasError() {
|
||||||
if r.HttpResponseCode() >= 400 && r.HttpResponseCode() < 500 {
|
if r.HttpResponseCode() >= 400 && r.HttpResponseCode() < 500 {
|
||||||
return nil, new(sftp_server.InvalidCredentialsError)
|
return nil, new(sftpInvalidCredentialsError)
|
||||||
}
|
}
|
||||||
|
|
||||||
rerr := errors.New(r.Error().String())
|
rerr := errors.New(r.Error().String())
|
||||||
@@ -30,7 +76,7 @@ func (r *PanelRequest) ValidateSftpCredentials(request sftp_server.Authenticatio
|
|||||||
return nil, rerr
|
return nil, rerr
|
||||||
}
|
}
|
||||||
|
|
||||||
response := new(sftp_server.AuthenticationResponse)
|
response := new(SftpAuthResponse)
|
||||||
body, _ := r.ReadBody()
|
body, _ := r.ReadBody()
|
||||||
|
|
||||||
if err := json.Unmarshal(body, response); err != nil {
|
if err := json.Unmarshal(body, response); err != nil {
|
||||||
|
|||||||
@@ -218,9 +218,8 @@ func rootCmdRun(*cobra.Command, []string) {
|
|||||||
pool.StopWait()
|
pool.StopWait()
|
||||||
|
|
||||||
// Initialize the SFTP server.
|
// Initialize the SFTP server.
|
||||||
if err := sftp.Initialize(c); err != nil {
|
if err := sftp.Initialize(c.System); err != nil {
|
||||||
log.WithError(err).Error("failed to initialize the sftp server")
|
log.WithError(err).Fatal("failed to initialize the sftp server")
|
||||||
os.Exit(1)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,6 +336,10 @@ func Execute() error {
|
|||||||
// Configures the global logger for Zap so that we can call it from any location
|
// 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.
|
// in the code without having to pass around a logger instance.
|
||||||
func configureLogging(logDir string, debug bool) error {
|
func configureLogging(logDir string, debug bool) error {
|
||||||
|
if err := os.MkdirAll(path.Join(logDir, "/install"), 0700); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
cfg := zap.NewProductionConfig()
|
cfg := zap.NewProductionConfig()
|
||||||
if debug {
|
if debug {
|
||||||
cfg = zap.NewDevelopmentConfig()
|
cfg = zap.NewDevelopmentConfig()
|
||||||
|
|||||||
@@ -38,6 +38,12 @@ type SystemConfiguration struct {
|
|||||||
// the user did not press the stop button, but the process stopped cleanly.
|
// the user did not press the stop button, but the process stopped cleanly.
|
||||||
DetectCleanExitAsCrash bool `default:"true" yaml:"detect_clean_exit_as_crash"`
|
DetectCleanExitAsCrash bool `default:"true" yaml:"detect_clean_exit_as_crash"`
|
||||||
|
|
||||||
|
// If set to true, file permissions for a server will be checked when the process is
|
||||||
|
// booted. This can cause boot delays if the server has a large amount of files. In most
|
||||||
|
// cases disabling this should not have any major impact unless external processes are
|
||||||
|
// frequently modifying a servers' files.
|
||||||
|
CheckPermissionsOnBoot bool `default:"true" yaml:"check_permissions_on_boot"`
|
||||||
|
|
||||||
Sftp SftpConfiguration `yaml:"sftp"`
|
Sftp SftpConfiguration `yaml:"sftp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,11 +55,6 @@ func (sc *SystemConfiguration) ConfigureDirectories() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.WithField("path", sc.LogDirectory).Debug("ensuring log directory exists")
|
|
||||||
if err := os.MkdirAll(path.Join(sc.LogDirectory, "/install"), 0700); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.WithField("path", sc.Data).Debug("ensuring server data directory exists")
|
log.WithField("path", sc.Data).Debug("ensuring server data directory exists")
|
||||||
if err := os.MkdirAll(sc.Data, 0700); err != nil {
|
if err := os.MkdirAll(sc.Data, 0700); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
package environment
|
package environment
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type configurationSettings struct {
|
type Settings struct {
|
||||||
Mounts []Mount
|
Mounts []Mount
|
||||||
Allocations Allocations
|
Allocations Allocations
|
||||||
Limits Limits
|
Limits Limits
|
||||||
Variables Variables
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defines the actual configuration struct for the environment with all of the settings
|
// Defines the actual configuration struct for the environment with all of the settings
|
||||||
@@ -19,20 +15,36 @@ type configurationSettings struct {
|
|||||||
type Configuration struct {
|
type Configuration struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
|
||||||
settings configurationSettings
|
environmentVariables []string
|
||||||
|
settings Settings
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfiguration(m []Mount, a Allocations, l Limits, v Variables) *Configuration {
|
// Returns a new environment configuration with the given settings and environment variables
|
||||||
|
// defined within it.
|
||||||
|
func NewConfiguration(s Settings, envVars []string) *Configuration {
|
||||||
return &Configuration{
|
return &Configuration{
|
||||||
settings: configurationSettings{
|
environmentVariables: envVars,
|
||||||
Mounts: m,
|
settings: s,
|
||||||
Allocations: a,
|
|
||||||
Limits: l,
|
|
||||||
Variables: v,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Updates the settings struct for this environment on the fly. This allows modified servers to
|
||||||
|
// automatically push those changes to the environment.
|
||||||
|
func (c *Configuration) SetSettings(s Settings) {
|
||||||
|
c.mu.Lock()
|
||||||
|
c.settings = s
|
||||||
|
c.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates the environment variables associated with this environment by replacing the entire
|
||||||
|
// array of them with a new one.
|
||||||
|
func (c *Configuration) SetEnvironmentVariables(ev []string) {
|
||||||
|
c.mu.Lock()
|
||||||
|
c.environmentVariables = ev
|
||||||
|
c.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the limits assigned to this environment.
|
||||||
func (c *Configuration) Limits() Limits {
|
func (c *Configuration) Limits() Limits {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
defer c.mu.RUnlock()
|
defer c.mu.RUnlock()
|
||||||
@@ -40,6 +52,7 @@ func (c *Configuration) Limits() Limits {
|
|||||||
return c.settings.Limits
|
return c.settings.Limits
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rturns the allocations associated with this environment.
|
||||||
func (c *Configuration) Allocations() Allocations {
|
func (c *Configuration) Allocations() Allocations {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
defer c.mu.RUnlock()
|
defer c.mu.RUnlock()
|
||||||
@@ -47,6 +60,7 @@ func (c *Configuration) Allocations() Allocations {
|
|||||||
return c.settings.Allocations
|
return c.settings.Allocations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns all of the mounts associated with this environment.
|
||||||
func (c *Configuration) Mounts() []Mount {
|
func (c *Configuration) Mounts() []Mount {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
defer c.mu.RUnlock()
|
defer c.mu.RUnlock()
|
||||||
@@ -54,31 +68,10 @@ func (c *Configuration) Mounts() []Mount {
|
|||||||
return c.settings.Mounts
|
return c.settings.Mounts
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns all of the environment variables that should be assigned to a running
|
// Returns the environment variables associated with this instance.
|
||||||
// server instance.
|
|
||||||
func (c *Configuration) EnvironmentVariables() []string {
|
func (c *Configuration) EnvironmentVariables() []string {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
c.mu.RUnlock()
|
defer c.mu.RUnlock()
|
||||||
|
|
||||||
zone, _ := time.Now().In(time.Local).Zone()
|
return c.environmentVariables
|
||||||
|
|
||||||
var out = []string{
|
|
||||||
fmt.Sprintf("TZ=%s", zone),
|
|
||||||
fmt.Sprintf("SERVER_MEMORY=%d", c.settings.Limits.MemoryLimit),
|
|
||||||
fmt.Sprintf("SERVER_IP=%s", c.settings.Allocations.DefaultMapping.Ip),
|
|
||||||
fmt.Sprintf("SERVER_PORT=%d", c.settings.Allocations.DefaultMapping.Port),
|
|
||||||
}
|
|
||||||
|
|
||||||
eloop:
|
|
||||||
for k := range c.settings.Variables {
|
|
||||||
for _, e := range out {
|
|
||||||
if strings.HasPrefix(e, strings.ToUpper(k)) {
|
|
||||||
continue eloop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out = append(out, fmt.Sprintf("%s=%s", strings.ToUpper(k), c.settings.Variables.Get(k)))
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ func (e *Environment) Create() error {
|
|||||||
Tty: true,
|
Tty: true,
|
||||||
ExposedPorts: a.Exposed(),
|
ExposedPorts: a.Exposed(),
|
||||||
Image: e.meta.Image,
|
Image: e.meta.Image,
|
||||||
Env: e.variables(),
|
Env: e.Configuration.EnvironmentVariables(),
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
"Service": "Pterodactyl",
|
"Service": "Pterodactyl",
|
||||||
"ContainerType": "server_process",
|
"ContainerType": "server_process",
|
||||||
@@ -204,12 +204,6 @@ func (e *Environment) Create() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Environment) variables() []string {
|
|
||||||
v := e.Configuration.EnvironmentVariables()
|
|
||||||
|
|
||||||
return append(v, fmt.Sprintf("STARTUP=%s", e.meta.Invocation))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Environment) convertMounts() []mount.Mount {
|
func (e *Environment) convertMounts() []mount.Mount {
|
||||||
var out []mount.Mount
|
var out []mount.Mount
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Metadata struct {
|
type Metadata struct {
|
||||||
Invocation string
|
|
||||||
Image string
|
Image string
|
||||||
Stop *api.ProcessStopConfiguration
|
Stop *api.ProcessStopConfiguration
|
||||||
}
|
}
|
||||||
@@ -71,12 +70,6 @@ func New(id string, m *Metadata, c *environment.Configuration) (*Environment, er
|
|||||||
return e, nil
|
return e, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Environment) SetStopConfiguration(c *api.ProcessStopConfiguration) {
|
|
||||||
e.mu.Lock()
|
|
||||||
e.meta.Stop = c
|
|
||||||
e.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Environment) Type() string {
|
func (e *Environment) Type() string {
|
||||||
return "docker"
|
return "docker"
|
||||||
}
|
}
|
||||||
@@ -167,3 +160,19 @@ func (e *Environment) ExitState() (uint32, bool, error) {
|
|||||||
|
|
||||||
return uint32(c.State.ExitCode), c.State.OOMKilled, nil
|
return uint32(c.State.ExitCode), c.State.OOMKilled, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the environment configuration allowing a process to make modifications of the
|
||||||
|
// environment on the fly.
|
||||||
|
func (e *Environment) Config() *environment.Configuration {
|
||||||
|
e.mu.RLock()
|
||||||
|
defer e.mu.RUnlock()
|
||||||
|
|
||||||
|
return e.Configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the stop configuration for the environment.
|
||||||
|
func (e *Environment) SetStopConfiguration(c *api.ProcessStopConfiguration) {
|
||||||
|
e.mu.Lock()
|
||||||
|
e.meta.Stop = c
|
||||||
|
e.mu.Unlock()
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ func (e *Environment) OnBeforeStart() error {
|
|||||||
// the Panel is usee.
|
// the Panel is usee.
|
||||||
if err := e.client.ContainerRemove(context.Background(), e.Id, types.ContainerRemoveOptions{RemoveVolumes: true}); err != nil {
|
if err := e.client.ContainerRemove(context.Background(), e.Id, types.ContainerRemoveOptions{RemoveVolumes: true}); err != nil {
|
||||||
if !client.IsErrNotFound(err) {
|
if !client.IsErrNotFound(err) {
|
||||||
return err
|
return errors.Wrap(err, "failed to remove server docker container during pre-boot")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ type ProcessEnvironment interface {
|
|||||||
// Returns the name of the environment.
|
// Returns the name of the environment.
|
||||||
Type() string
|
Type() string
|
||||||
|
|
||||||
|
// Returns the environment configuration to the caller.
|
||||||
|
Config() *Configuration
|
||||||
|
|
||||||
// Returns an event emitter instance that can be hooked into to listen for different
|
// Returns an event emitter instance that can be hooked into to listen for different
|
||||||
// events that are fired by the environment. This should not allow someone to publish
|
// events that are fired by the environment. This should not allow someone to publish
|
||||||
// events, only subscribe to them.
|
// events, only subscribe to them.
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package environment
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/apex/log"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
@@ -118,7 +119,13 @@ func (v Variables) Get(key string) string {
|
|||||||
return fmt.Sprintf("%f", val.(float64))
|
return fmt.Sprintf("%f", val.(float64))
|
||||||
case bool:
|
case bool:
|
||||||
return strconv.FormatBool(val.(bool))
|
return strconv.FormatBool(val.(bool))
|
||||||
|
case string:
|
||||||
|
return val.(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
return val.(string)
|
// TODO: I think we can add a check for val == nil and return an empty string for those
|
||||||
|
// and this warning should theoretically never happen?
|
||||||
|
log.Warn(fmt.Sprintf("failed to marshal environment variable \"%s\" of type %+v into string", key, val))
|
||||||
|
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|||||||
17
go.mod
17
go.mod
@@ -2,14 +2,6 @@ module github.com/pterodactyl/wings
|
|||||||
|
|
||||||
go 1.13
|
go 1.13
|
||||||
|
|
||||||
// Uncomment this in development environments to make changes to the core SFTP
|
|
||||||
// server software. This assumes you're using the official Pterodactyl Environment
|
|
||||||
// otherwise this path will not work.
|
|
||||||
//
|
|
||||||
// @see https://github.com/pterodactyl/development
|
|
||||||
//
|
|
||||||
// replace github.com/pterodactyl/sftp-server => ../sftp-server
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AlecAivazis/survey/v2 v2.1.0
|
github.com/AlecAivazis/survey/v2 v2.1.0
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
|
||||||
@@ -32,6 +24,7 @@ require (
|
|||||||
github.com/docker/go-metrics v0.0.1 // indirect
|
github.com/docker/go-metrics v0.0.1 // indirect
|
||||||
github.com/docker/go-units v0.4.0 // indirect
|
github.com/docker/go-units v0.4.0 // indirect
|
||||||
github.com/fatih/color v1.9.0
|
github.com/fatih/color v1.9.0
|
||||||
|
github.com/frankban/quicktest v1.10.2 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.1.1
|
github.com/gabriel-vasile/mimetype v1.1.1
|
||||||
github.com/gammazero/deque v0.0.0-20200721202602-07291166fe33 // indirect
|
github.com/gammazero/deque v0.0.0-20200721202602-07291166fe33 // indirect
|
||||||
@@ -40,13 +33,13 @@ require (
|
|||||||
github.com/gin-gonic/gin v1.6.3
|
github.com/gin-gonic/gin v1.6.3
|
||||||
github.com/go-playground/validator/v10 v10.3.0 // indirect
|
github.com/go-playground/validator/v10 v10.3.0 // indirect
|
||||||
github.com/gogo/protobuf v1.3.1 // indirect
|
github.com/gogo/protobuf v1.3.1 // indirect
|
||||||
github.com/golang/gddo v0.0.0-20200715224205-051695c33a3f // indirect
|
|
||||||
github.com/google/uuid v1.1.1
|
github.com/google/uuid v1.1.1
|
||||||
github.com/gorilla/mux v1.7.4 // indirect
|
github.com/gorilla/mux v1.7.4 // indirect
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334
|
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334
|
||||||
github.com/icza/dyno v0.0.0-20200205103839-49cb13720835
|
github.com/icza/dyno v0.0.0-20200205103839-49cb13720835
|
||||||
github.com/imdario/mergo v0.3.8
|
github.com/imdario/mergo v0.3.8
|
||||||
|
github.com/karrick/godirwalk v1.16.1
|
||||||
github.com/klauspost/compress v1.10.10 // indirect
|
github.com/klauspost/compress v1.10.10 // indirect
|
||||||
github.com/klauspost/pgzip v1.2.4
|
github.com/klauspost/pgzip v1.2.4
|
||||||
github.com/magefile/mage v1.10.0 // indirect
|
github.com/magefile/mage v1.10.0 // indirect
|
||||||
@@ -65,15 +58,12 @@ require (
|
|||||||
github.com/pierrec/lz4 v2.5.2+incompatible // indirect
|
github.com/pierrec/lz4 v2.5.2+incompatible // indirect
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/pkg/profile v1.5.0
|
github.com/pkg/profile v1.5.0
|
||||||
github.com/pkg/sftp v1.11.0 // indirect
|
github.com/pkg/sftp v1.11.0
|
||||||
github.com/prometheus/common v0.11.1 // indirect
|
github.com/prometheus/common v0.11.1 // indirect
|
||||||
github.com/pterodactyl/sftp-server v1.1.5
|
|
||||||
github.com/remeh/sizedwaitgroup v1.0.0
|
github.com/remeh/sizedwaitgroup v1.0.0
|
||||||
github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94
|
github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94
|
||||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
|
||||||
github.com/spf13/cobra v1.0.0
|
github.com/spf13/cobra v1.0.0
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/uber-go/zap v1.9.1 // indirect
|
|
||||||
github.com/ulikunitz/xz v0.5.7 // indirect
|
github.com/ulikunitz/xz v0.5.7 // indirect
|
||||||
go.uber.org/zap v1.15.0
|
go.uber.org/zap v1.15.0
|
||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
|
||||||
@@ -83,7 +73,6 @@ require (
|
|||||||
golang.org/x/text v0.3.3 // indirect
|
golang.org/x/text v0.3.3 // indirect
|
||||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
|
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
|
||||||
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5 // indirect
|
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5 // indirect
|
||||||
golang.org/x/tools/gopls v0.1.3 // indirect
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98 // indirect
|
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98 // indirect
|
||||||
google.golang.org/grpc v1.31.0 // indirect
|
google.golang.org/grpc v1.31.0 // indirect
|
||||||
|
|||||||
113
go.sum
113
go.sum
@@ -1,21 +1,14 @@
|
|||||||
cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
github.com/AlecAivazis/survey/v2 v2.0.7 h1:+f825XHLse/hWd2tE/V5df04WFGimk34Eyg/z35w/rc=
|
|
||||||
github.com/AlecAivazis/survey/v2 v2.0.7/go.mod h1:mlizQTaPjnR4jcpwRSaSlkbsRfYFEyKgLQvYTzxxiHA=
|
|
||||||
github.com/AlecAivazis/survey/v2 v2.1.0 h1:AT4+23hOFopXYZaNGugbk7MWItkz0SfTmH/Hk92KeeE=
|
github.com/AlecAivazis/survey/v2 v2.1.0 h1:AT4+23hOFopXYZaNGugbk7MWItkz0SfTmH/Hk92KeeE=
|
||||||
github.com/AlecAivazis/survey/v2 v2.1.0/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
|
github.com/AlecAivazis/survey/v2 v2.1.0/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Jeffail/gabs/v2 v2.2.0 h1:7touC+WzbQ7LO5+mwgxT44miyTqAVCOlIWLA6PiIB5w=
|
|
||||||
github.com/Jeffail/gabs/v2 v2.2.0/go.mod h1:xCn81vdHKxFUuWWAaD5jCTQDNPBMh5pPs9IJ+NcziBI=
|
|
||||||
github.com/Jeffail/gabs/v2 v2.5.1 h1:ANfZYjpMlfTTKebycu4X1AgkVWumFVDYQl7JwOr4mDk=
|
github.com/Jeffail/gabs/v2 v2.5.1 h1:ANfZYjpMlfTTKebycu4X1AgkVWumFVDYQl7JwOr4mDk=
|
||||||
github.com/Jeffail/gabs/v2 v2.5.1/go.mod h1:xCn81vdHKxFUuWWAaD5jCTQDNPBMh5pPs9IJ+NcziBI=
|
github.com/Jeffail/gabs/v2 v2.5.1/go.mod h1:xCn81vdHKxFUuWWAaD5jCTQDNPBMh5pPs9IJ+NcziBI=
|
||||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||||
github.com/Microsoft/go-winio v0.4.7 h1:vOvDiY/F1avSWlCWiKJjdYKz2jVjTK3pWPHndeG4OAY=
|
|
||||||
github.com/Microsoft/go-winio v0.4.7/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
|
||||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||||
github.com/NYTimes/logrotate v1.0.0 h1:6jFGbon6jOtpy3t3kwZZKS4Gdmf1C/Wv5J4ll4Xn5yk=
|
github.com/NYTimes/logrotate v1.0.0 h1:6jFGbon6jOtpy3t3kwZZKS4Gdmf1C/Wv5J4ll4Xn5yk=
|
||||||
@@ -38,11 +31,8 @@ github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDa
|
|||||||
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||||
github.com/apex/log v1.3.0 h1:1fyfbPvUwD10nMoh3hY6MXzvZShJQn9/ck7ATgAt5pA=
|
|
||||||
github.com/apex/log v1.3.0/go.mod h1:jd8Vpsr46WAe3EZSQ/IUMs2qQD/GOycT5rPWCO1yGcs=
|
|
||||||
github.com/apex/log v1.8.0 h1:+W4j+dttibFvynPLlctdnYFUn1eLKT37BZWWW2iMfEM=
|
github.com/apex/log v1.8.0 h1:+W4j+dttibFvynPLlctdnYFUn1eLKT37BZWWW2iMfEM=
|
||||||
github.com/apex/log v1.8.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA=
|
github.com/apex/log v1.8.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA=
|
||||||
github.com/apex/logs v0.0.4/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo=
|
|
||||||
github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo=
|
github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo=
|
||||||
github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE=
|
github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE=
|
||||||
github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys=
|
github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys=
|
||||||
@@ -51,8 +41,6 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5
|
|||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
|
|
||||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
|
||||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY=
|
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||||
@@ -68,9 +56,6 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
|
|||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
|
|
||||||
github.com/buger/jsonparser v0.0.0-20191204142016-1a29609e0929 h1:MW/JDk68Rny52yI0M0N+P8lySNgB+NhpI/uAmhgOhUM=
|
|
||||||
github.com/buger/jsonparser v0.0.0-20191204142016-1a29609e0929/go.mod h1:tgcrVJ81GPSF0mz+0nu1Xaz0fazGPrmmJfJtxjbHhUQ=
|
|
||||||
github.com/buger/jsonparser v1.0.0 h1:etJTGF5ESxjI0Ic2UaLQs2LQQpa8G9ykQScukbh4L8A=
|
github.com/buger/jsonparser v1.0.0 h1:etJTGF5ESxjI0Ic2UaLQs2LQQpa8G9ykQScukbh4L8A=
|
||||||
github.com/buger/jsonparser v1.0.0/go.mod h1:tgcrVJ81GPSF0mz+0nu1Xaz0fazGPrmmJfJtxjbHhUQ=
|
github.com/buger/jsonparser v1.0.0/go.mod h1:tgcrVJ81GPSF0mz+0nu1Xaz0fazGPrmmJfJtxjbHhUQ=
|
||||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||||
@@ -87,12 +72,8 @@ github.com/cobaugh/osrelease v0.0.0-20181218015638-a93a0a55a249 h1:R0IDH8daQ3lOD
|
|||||||
github.com/cobaugh/osrelease v0.0.0-20181218015638-a93a0a55a249/go.mod h1:EHKW9yNEYSBpTKzuu7Y9oOrft/UlzH57rMIB03oev6M=
|
github.com/cobaugh/osrelease v0.0.0-20181218015638-a93a0a55a249/go.mod h1:EHKW9yNEYSBpTKzuu7Y9oOrft/UlzH57rMIB03oev6M=
|
||||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||||
github.com/containerd/containerd v1.3.6 h1:SMfcKoQyWhaRsYq7290ioC6XFcHDNcHvcEMjF6ORpac=
|
|
||||||
github.com/containerd/containerd v1.3.6/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
|
||||||
github.com/containerd/containerd v1.3.7 h1:eFSOChY8TTcxvkzp8g+Ov1RL0MYww7XEeK0y+zqGpVc=
|
github.com/containerd/containerd v1.3.7 h1:eFSOChY8TTcxvkzp8g+Ov1RL0MYww7XEeK0y+zqGpVc=
|
||||||
github.com/containerd/containerd v1.3.7/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
github.com/containerd/containerd v1.3.7/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||||
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448 h1:PUD50EuOMkXVcpBIA/R95d56duJR9VxhwncsFbNnxW4=
|
|
||||||
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
|
|
||||||
github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b h1:qUtCegLdOUVfVJOw+KDg6eJyE1TGvLlkGEd1091kSSQ=
|
github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b h1:qUtCegLdOUVfVJOw+KDg6eJyE1TGvLlkGEd1091kSSQ=
|
||||||
github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
|
github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
|
||||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||||
@@ -105,8 +86,6 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
|
|||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/creasty/defaults v1.3.0 h1:uG+RAxYbJgOPCOdKEcec9ZJXeva7Y6mj/8egdzwmLtw=
|
|
||||||
github.com/creasty/defaults v1.3.0/go.mod h1:CIEEvs7oIVZm30R8VxtFJs+4k201gReYyuYHJxZc68I=
|
|
||||||
github.com/creasty/defaults v1.5.0 h1:DW6NAGGaKuNSKkntc8BCBrR2KOUAcXVnfcwu/LmJhaQ=
|
github.com/creasty/defaults v1.5.0 h1:DW6NAGGaKuNSKkntc8BCBrR2KOUAcXVnfcwu/LmJhaQ=
|
||||||
github.com/creasty/defaults v1.5.0/go.mod h1:FPZ+Y0WNrbqOVw+c6av63eyHUAl6pMHZwqLPvXUZGfY=
|
github.com/creasty/defaults v1.5.0/go.mod h1:FPZ+Y0WNrbqOVw+c6av63eyHUAl6pMHZwqLPvXUZGfY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -124,8 +103,6 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh
|
|||||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
|
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
|
||||||
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
|
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
|
||||||
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
|
|
||||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
|
||||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
|
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
|
||||||
@@ -147,26 +124,20 @@ github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
|||||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||||
github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/frankban/quicktest v1.10.2 h1:19ARM85nVi4xH7xPXuc5eM/udya5ieh7b/Sv+d844Tk=
|
||||||
|
github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s=
|
||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/gabriel-vasile/mimetype v0.1.4 h1:5mcsq3+DXypREUkW+1juhjeKmE/XnWgs+paHMJn7lf8=
|
|
||||||
github.com/gabriel-vasile/mimetype v0.1.4/go.mod h1:kMJbg3SlWZCsj4R73F1WDzbT9AyGCOVmUtIxxwO5pmI=
|
|
||||||
github.com/gabriel-vasile/mimetype v1.1.1 h1:qbN9MPuRf3bstHu9zkI9jDWNfH//9+9kHxr9oRBBBOA=
|
github.com/gabriel-vasile/mimetype v1.1.1 h1:qbN9MPuRf3bstHu9zkI9jDWNfH//9+9kHxr9oRBBBOA=
|
||||||
github.com/gabriel-vasile/mimetype v1.1.1/go.mod h1:6CDPel/o/3/s4+bp6kIbsWATq8pmgOisOPG40CJa6To=
|
github.com/gabriel-vasile/mimetype v1.1.1/go.mod h1:6CDPel/o/3/s4+bp6kIbsWATq8pmgOisOPG40CJa6To=
|
||||||
github.com/gammazero/deque v0.0.0-20200227231300-1e9af0e52b46 h1:iX4+rD9Fjdx8SkmSO/O5WAIX/j79ll3kuqv5VdYt9J8=
|
github.com/gammazero/deque v0.0.0-20200227231300-1e9af0e52b46 h1:iX4+rD9Fjdx8SkmSO/O5WAIX/j79ll3kuqv5VdYt9J8=
|
||||||
github.com/gammazero/deque v0.0.0-20200227231300-1e9af0e52b46/go.mod h1:D90+MBHVc9Sk1lJAbEVgws0eYEurY4mv2TDso3Nxh3w=
|
github.com/gammazero/deque v0.0.0-20200227231300-1e9af0e52b46/go.mod h1:D90+MBHVc9Sk1lJAbEVgws0eYEurY4mv2TDso3Nxh3w=
|
||||||
github.com/gammazero/deque v0.0.0-20200721202602-07291166fe33 h1:UG4wNrJX9xSKnm/Gck5yTbxnOhpNleuE4MQRdmcGySo=
|
github.com/gammazero/deque v0.0.0-20200721202602-07291166fe33 h1:UG4wNrJX9xSKnm/Gck5yTbxnOhpNleuE4MQRdmcGySo=
|
||||||
github.com/gammazero/deque v0.0.0-20200721202602-07291166fe33/go.mod h1:D90+MBHVc9Sk1lJAbEVgws0eYEurY4mv2TDso3Nxh3w=
|
github.com/gammazero/deque v0.0.0-20200721202602-07291166fe33/go.mod h1:D90+MBHVc9Sk1lJAbEVgws0eYEurY4mv2TDso3Nxh3w=
|
||||||
github.com/gammazero/workerpool v0.0.0-20200608033439-1a5ca90a5753 h1:oSQ61LxZkz3Z4La0O5cbyVDvLWEfbNgiD43cSPdjPQQ=
|
|
||||||
github.com/gammazero/workerpool v0.0.0-20200608033439-1a5ca90a5753/go.mod h1:/XWO2YAUUpPi3smDlFBl0vpX0JHwUomDM/oRMwRmnSs=
|
|
||||||
github.com/gammazero/workerpool v1.0.0 h1:MfkJc6KL0tAmjrRDS203AZz3F+84Uod9YbL8KjpcQ00=
|
github.com/gammazero/workerpool v1.0.0 h1:MfkJc6KL0tAmjrRDS203AZz3F+84Uod9YbL8KjpcQ00=
|
||||||
github.com/gammazero/workerpool v1.0.0/go.mod h1:/XWO2YAUUpPi3smDlFBl0vpX0JHwUomDM/oRMwRmnSs=
|
github.com/gammazero/workerpool v1.0.0/go.mod h1:/XWO2YAUUpPi3smDlFBl0vpX0JHwUomDM/oRMwRmnSs=
|
||||||
github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
|
||||||
github.com/gbrlsnchs/jwt/v3 v3.0.0-rc.0 h1:7KeiSrO5puFH1+vdAdbpiie2TrNnkvFc/eOQzT60Z2k=
|
|
||||||
github.com/gbrlsnchs/jwt/v3 v3.0.0-rc.0/go.mod h1:D1+3UtCYAJ1os1PI+zhTVEj6Tb+IHJvXjXKz83OstmM=
|
|
||||||
github.com/gbrlsnchs/jwt/v3 v3.0.0-rc.2 h1:3t7jvTkeQfk1FdP0noXSNiM6AdBokLz7QmZDmnCHAAA=
|
github.com/gbrlsnchs/jwt/v3 v3.0.0-rc.2 h1:3t7jvTkeQfk1FdP0noXSNiM6AdBokLz7QmZDmnCHAAA=
|
||||||
github.com/gbrlsnchs/jwt/v3 v3.0.0-rc.2/go.mod h1:AncDcjXz18xetI3A6STfXq2w+LuTx8pQ8bGEwRN8zVM=
|
github.com/gbrlsnchs/jwt/v3 v3.0.0-rc.2/go.mod h1:AncDcjXz18xetI3A6STfXq2w+LuTx8pQ8bGEwRN8zVM=
|
||||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
@@ -193,7 +164,6 @@ github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GO
|
|||||||
github.com/go-playground/validator/v10 v10.3.0 h1:nZU+7q+yJoFmwvNgv/LnPUkwPal62+b2xXj0AU1Es7o=
|
github.com/go-playground/validator/v10 v10.3.0 h1:nZU+7q+yJoFmwvNgv/LnPUkwPal62+b2xXj0AU1Es7o=
|
||||||
github.com/go-playground/validator/v10 v10.3.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
github.com/go-playground/validator/v10 v10.3.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
@@ -204,20 +174,16 @@ github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
|||||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721 h1:KRMr9A3qfbVM7iV/WcLY/rL5LICqwMHLhwRXKu99fXw=
|
github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721 h1:KRMr9A3qfbVM7iV/WcLY/rL5LICqwMHLhwRXKu99fXw=
|
||||||
github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4=
|
github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4=
|
||||||
github.com/golang/gddo v0.0.0-20200715224205-051695c33a3f/go.mod h1:sam69Hju0uq+5uvLJUMDlsKlQ21Vrs1Kd/1YFPNYdOU=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/lint v0.0.0-20170918230701-e5d664eb928e/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
|
|
||||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
@@ -226,25 +192,25 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
|||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.1.1-0.20171103154506-982329095285/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||||
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
@@ -257,7 +223,6 @@ github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH
|
|||||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gregjones/httpcache v0.0.0-20170920190843-316c5e0ff04e/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
@@ -279,7 +244,6 @@ 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/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.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/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/hcl v0.0.0-20170914154624-68e816d1c783/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
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/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=
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
@@ -295,9 +259,6 @@ github.com/icza/dyno v0.0.0-20200205103839-49cb13720835 h1:f1irK5f03uGGj+FjgQfZ5
|
|||||||
github.com/icza/dyno v0.0.0-20200205103839-49cb13720835/go.mod h1:c1tRKs5Tx7E2+uHGSyyncziFjvGpgv4H2HrqXeUQ/Uk=
|
github.com/icza/dyno v0.0.0-20200205103839-49cb13720835/go.mod h1:c1tRKs5Tx7E2+uHGSyyncziFjvGpgv4H2HrqXeUQ/Uk=
|
||||||
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/imdario/mergo v0.3.10 h1:6q5mVkdH/vYmqngx7kZQTjJ5HRsx+ImorDIEQ+beJgc=
|
|
||||||
github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
|
||||||
github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
|
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||||
@@ -317,6 +278,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
|||||||
github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
|
github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
|
github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw=
|
||||||
|
github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
@@ -330,8 +293,6 @@ github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdY
|
|||||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||||
github.com/klauspost/pgzip v1.2.1 h1:oIPZROsWuPHpOdMVWLuJZXwgjhrW8r1yEX8UqMyeNHM=
|
github.com/klauspost/pgzip v1.2.1 h1:oIPZROsWuPHpOdMVWLuJZXwgjhrW8r1yEX8UqMyeNHM=
|
||||||
github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||||
github.com/klauspost/pgzip v1.2.3 h1:Ce2to9wvs/cuJ2b86/CKQoTYr9VHfpanYosZ0UBJqdw=
|
|
||||||
github.com/klauspost/pgzip v1.2.3/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
|
||||||
github.com/klauspost/pgzip v1.2.4 h1:TQ7CNpYKovDOmqzRHKxJh0BeaBI7UdQZYc6p7pMQh1A=
|
github.com/klauspost/pgzip v1.2.4 h1:TQ7CNpYKovDOmqzRHKxJh0BeaBI7UdQZYc6p7pMQh1A=
|
||||||
github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||||
@@ -344,6 +305,8 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
|
|||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
|
github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
|
||||||
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
@@ -358,12 +321,10 @@ github.com/magefile/mage v1.9.0 h1:t3AU2wNwehMCW97vuqQLtw6puppWXHO+O2MHo5a50XE=
|
|||||||
github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
github.com/magefile/mage v1.10.0 h1:3HiXzCUY12kh9bIuyXShaVe529fJfyqoVM42o/uom2g=
|
github.com/magefile/mage v1.10.0 h1:3HiXzCUY12kh9bIuyXShaVe529fJfyqoVM42o/uom2g=
|
||||||
github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
github.com/magiconair/properties v1.7.4-0.20170902060319-8d7837e64d3c/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
|
||||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
@@ -371,7 +332,6 @@ github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaa
|
|||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
|
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
|
||||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.2/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
@@ -400,7 +360,6 @@ 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/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
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 v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
@@ -434,8 +393,6 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
|||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||||
@@ -453,10 +410,8 @@ 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 h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
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/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||||
github.com/pelletier/go-toml v1.0.1-0.20170904195809-1d6b12b7cb29/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
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/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||||
github.com/pierrec/lz4 v1.0.1 h1:w6GMGWSsCI04fTM8wQRdnW74MuJISakuUU0onU0TYB4=
|
|
||||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
|
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||||
@@ -469,12 +424,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||||
github.com/pkg/profile v1.4.0 h1:uCmaf4vVbWAOZz36k1hrQD7ijGRzLwaME8Am/7a4jZI=
|
|
||||||
github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE=
|
|
||||||
github.com/pkg/profile v1.5.0 h1:042Buzk+NhDI+DeSAA62RwJL8VAuZUMQZUjCsRz1Mug=
|
github.com/pkg/profile v1.5.0 h1:042Buzk+NhDI+DeSAA62RwJL8VAuZUMQZUjCsRz1Mug=
|
||||||
github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
|
github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
|
||||||
github.com/pkg/sftp v1.8.3 h1:9jSe2SxTM8/3bXZjtqnkgTBW+lA8db0knZJyns7gpBA=
|
|
||||||
github.com/pkg/sftp v1.8.3/go.mod h1:NxmoDg/QLVWluQDUYG7XBZTLUpKeFa8e3aMf1BfjyHk=
|
|
||||||
github.com/pkg/sftp v1.11.0 h1:4Zv0OGbpkg4yNuUtH0s8rvoYxRCNyT29NVUo6pgPmxI=
|
github.com/pkg/sftp v1.11.0 h1:4Zv0OGbpkg4yNuUtH0s8rvoYxRCNyT29NVUo6pgPmxI=
|
||||||
github.com/pkg/sftp v1.11.0/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
github.com/pkg/sftp v1.11.0/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
@@ -517,13 +468,7 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx
|
|||||||
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
|
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
|
||||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
github.com/pterodactyl/sftp-server v1.1.4 h1:JESuEuZ+d2tajMjuQblPOlGISM9Uc2xOzk7irVF9PQ0=
|
|
||||||
github.com/pterodactyl/sftp-server v1.1.4/go.mod h1:KjSONrenRr1oCh94QIVAU6yEzMe+Hd7r/JHrh5/oQHs=
|
|
||||||
github.com/pterodactyl/sftp-server v1.1.5 h1:r5RIfCDVLpn6MsfD8zcCQLtviy14GJ9E+9HzidjgAGw=
|
|
||||||
github.com/pterodactyl/sftp-server v1.1.5/go.mod h1:YVx5g2gjln7fYFO7+c3iDRTwNyA5GuJtkKME0UDB8co=
|
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
github.com/remeh/sizedwaitgroup v0.0.0-20180822144253-5e7302b12cce h1:aP+C+YbHZfOQlutA4p4soHi7rVUqHQdWEVMSkHfDTqY=
|
|
||||||
github.com/remeh/sizedwaitgroup v0.0.0-20180822144253-5e7302b12cce/go.mod h1:3j2R4OIe/SeS6YDhICBy22RWjJC5eNCJ1V+9+NVNYlo=
|
|
||||||
github.com/remeh/sizedwaitgroup v1.0.0 h1:VNGGFwNo/R5+MJBf6yrsr110p0m4/OX4S3DCy7Kyl5E=
|
github.com/remeh/sizedwaitgroup v1.0.0 h1:VNGGFwNo/R5+MJBf6yrsr110p0m4/OX4S3DCy7Kyl5E=
|
||||||
github.com/remeh/sizedwaitgroup v1.0.0/go.mod h1:3j2R4OIe/SeS6YDhICBy22RWjJC5eNCJ1V+9+NVNYlo=
|
github.com/remeh/sizedwaitgroup v1.0.0/go.mod h1:3j2R4OIe/SeS6YDhICBy22RWjJC5eNCJ1V+9+NVNYlo=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
@@ -554,24 +499,17 @@ 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/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
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/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spf13/afero v0.0.0-20170901052352-ee1bd8ee15a1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
github.com/spf13/cast v1.1.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
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 v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
github.com/spf13/cobra v0.0.7 h1:FfTH+vuMXOas8jmfb5/M7dzEYx7LpcLb7a0LPe34uOU=
|
|
||||||
github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
|
||||||
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
|
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
|
||||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||||
github.com/spf13/jwalterweatherman v0.0.0-20170901151539-12bd96e66386/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
github.com/spf13/pflag v1.0.1-0.20170901120850-7aff26db30c1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
|
||||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
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/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
|
||||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
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/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||||
@@ -583,11 +521,11 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
|||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/tj/assert v0.0.0-20171129193455-018094318fb0 h1:Rw8kxzWo1mr6FSaYXjQELRe88y2KdfynXdnK72rdjtA=
|
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=
|
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
|
||||||
|
github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk=
|
||||||
github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk=
|
github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk=
|
||||||
github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc=
|
github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc=
|
||||||
github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
|
github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
|
||||||
@@ -596,7 +534,6 @@ github.com/tj/go-spin v1.1.0 h1:lhdWZsvImxvZ3q1C5OIB7d72DuOwP4O2NdBg9PyzNds=
|
|||||||
github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=
|
github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/uber-go/zap v1.9.1/go.mod h1:GY+83l3yxBcBw2kmHu/sAWwItnTn+ynxHCRo+WiIQOY=
|
|
||||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
@@ -632,14 +569,11 @@ go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
|||||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||||
go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
|
|
||||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||||
go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
|
go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
|
||||||
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
|
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
@@ -647,11 +581,8 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88=
|
|
||||||
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
|
||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
@@ -660,7 +591,6 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk
|
|||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
@@ -688,15 +618,11 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 h1:Jcxah/M+oLZ/R4/z5RzfPzGbPXnVDPkEDtf2JnuxN+U=
|
|
||||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -705,8 +631,6 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
|
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -735,19 +659,15 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f h1:mOhmO9WsBaJCNmaZHPtHs9wOcdqdKCjF6OPJlmDM3KI=
|
|
||||||
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo=
|
||||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200806125547-5acd03effb82 h1:6cBnXxYO+CiRVrChvCosSv7magqTPbyAgz1M8iOv5wM=
|
|
||||||
golang.org/x/sys v0.0.0-20200806125547-5acd03effb82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
@@ -765,7 +685,6 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3
|
|||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190710153321-831012c29e42/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
|
||||||
golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
|
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
|
||||||
@@ -776,7 +695,6 @@ golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapK
|
|||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5 h1:MeC2gMlMdkd67dn17MEby3rGXRxZtWeiRXOnISfTQ74=
|
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5 h1:MeC2gMlMdkd67dn17MEby3rGXRxZtWeiRXOnISfTQ74=
|
||||||
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools/gopls v0.1.3/go.mod h1:vrCQzOKxvuiZLjCKSmbbov04oeBQQOb4VQqwYK2PWIY=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -784,13 +702,10 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IV
|
|||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
|
||||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
|
||||||
google.golang.org/genproto v0.0.0-20170918111702-1e559d0a00ee/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
@@ -800,7 +715,6 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98
|
|||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98 h1:LCO0fg4kb6WwkXQXRQQgUYsFeFb5taTX5WAx5O/Vt28=
|
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98 h1:LCO0fg4kb6WwkXQXRQQgUYsFeFb5taTX5WAx5O/Vt28=
|
||||||
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
||||||
@@ -838,8 +752,6 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
|
|||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
|
||||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
|
||||||
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
|
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
|
||||||
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
@@ -856,6 +768,7 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||||
|
|||||||
@@ -26,14 +26,11 @@ func New(data []byte) (*Installer, error) {
|
|||||||
return nil, NewValidationError("uuid provided was not in a valid format")
|
return nil, NewValidationError("uuid provided was not in a valid format")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !govalidator.IsUUIDv4(getString(data, "service", "egg")) {
|
|
||||||
return nil, NewValidationError("service egg provided was not in a valid format")
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := &server.Configuration{
|
cfg := &server.Configuration{
|
||||||
Uuid: getString(data, "uuid"),
|
Uuid: getString(data, "uuid"),
|
||||||
Suspended: false,
|
Suspended: false,
|
||||||
Invocation: getString(data, "invocation"),
|
Invocation: getString(data, "invocation"),
|
||||||
|
SkipEggScripts: getBoolean(data, "skip_egg_scripts"),
|
||||||
Build: environment.Limits{
|
Build: environment.Limits{
|
||||||
MemoryLimit: getInt(data, "build", "memory"),
|
MemoryLimit: getInt(data, "build", "memory"),
|
||||||
Swap: getInt(data, "build", "swap"),
|
Swap: getInt(data, "build", "swap"),
|
||||||
@@ -117,7 +114,6 @@ func (i *Installer) Execute() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
l.Debug("creating required environment for server instance")
|
l.Debug("creating required environment for server instance")
|
||||||
// TODO: ensure data directory exists.
|
|
||||||
if err := i.server.Environment.Create(); err != nil {
|
if err := i.server.Environment.Create(); err != nil {
|
||||||
l.WithField("error", err).Error("failed to create environment for server")
|
l.WithField("error", err).Error("failed to create environment for server")
|
||||||
return
|
return
|
||||||
@@ -139,3 +135,9 @@ func getInt(data []byte, key ...string) int64 {
|
|||||||
|
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getBoolean(data []byte, key ...string) bool {
|
||||||
|
value, _ := jsonparser.GetBoolean(data, key...)
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package router
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"errors"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/pterodactyl/wings/router/tokens"
|
"github.com/pterodactyl/wings/router/tokens"
|
||||||
"github.com/pterodactyl/wings/server/backup"
|
"github.com/pterodactyl/wings/server/backup"
|
||||||
@@ -28,7 +29,7 @@ func getDownloadBackup(c *gin.Context) {
|
|||||||
|
|
||||||
b, st, err := backup.LocateLocal(token.BackupUuid)
|
b, st, err := backup.LocateLocal(token.BackupUuid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
|
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
|
||||||
"error": "The requested backup was not found on this server.",
|
"error": "The requested backup was not found on this server.",
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,13 +12,19 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type serverProcData struct {
|
||||||
|
server.ResourceUsage
|
||||||
|
Suspended bool `json:"suspended"`
|
||||||
|
}
|
||||||
|
|
||||||
// Returns a single server from the collection of servers.
|
// Returns a single server from the collection of servers.
|
||||||
func getServer(c *gin.Context) {
|
func getServer(c *gin.Context) {
|
||||||
s := GetServer(c.Param("server"))
|
s := GetServer(c.Param("server"))
|
||||||
|
|
||||||
p := *s.Proc()
|
c.JSON(http.StatusOK, serverProcData{
|
||||||
|
ResourceUsage: *s.Proc(),
|
||||||
c.JSON(http.StatusOK, p)
|
Suspended: s.IsSuspended(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the logs for a given server instance.
|
// Returns the logs for a given server instance.
|
||||||
@@ -50,7 +56,7 @@ func getServerLogs(c *gin.Context) {
|
|||||||
func postServerPower(c *gin.Context) {
|
func postServerPower(c *gin.Context) {
|
||||||
s := GetServer(c.Param("server"))
|
s := GetServer(c.Param("server"))
|
||||||
|
|
||||||
var data struct{
|
var data struct {
|
||||||
Action server.PowerAction `json:"action"`
|
Action server.PowerAction `json:"action"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,11 +140,13 @@ func patchServer(c *gin.Context) {
|
|||||||
buf := bytes.Buffer{}
|
buf := bytes.Buffer{}
|
||||||
buf.ReadFrom(c.Request.Body)
|
buf.ReadFrom(c.Request.Body)
|
||||||
|
|
||||||
if err := s.UpdateDataStructure(buf.Bytes(), true); err != nil {
|
if err := s.UpdateDataStructure(buf.Bytes()); err != nil {
|
||||||
TrackedServerError(err, s).AbortWithServerError(c)
|
TrackedServerError(err, s).AbortWithServerError(c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.SyncWithEnvironment()
|
||||||
|
|
||||||
c.Status(http.StatusNoContent)
|
c.Status(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +214,7 @@ func deleteServer(c *gin.Context) {
|
|||||||
go func(p string) {
|
go func(p string) {
|
||||||
if err := os.RemoveAll(p); err != nil {
|
if err := os.RemoveAll(p); err != nil {
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"path": p,
|
"path": p,
|
||||||
"error": errors.WithStack(err),
|
"error": errors.WithStack(err),
|
||||||
}).Warn("failed to remove server files during deletion process")
|
}).Warn("failed to remove server files during deletion process")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/pterodactyl/wings/server"
|
"github.com/pterodactyl/wings/server"
|
||||||
"github.com/pterodactyl/wings/server/backup"
|
"github.com/pterodactyl/wings/server/backup"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Backs up a server.
|
// Backs up a server.
|
||||||
@@ -46,19 +47,34 @@ func postServerBackup(c *gin.Context) {
|
|||||||
c.Status(http.StatusAccepted)
|
c.Status(http.StatusAccepted)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deletes a local backup of a server.
|
// Deletes a local backup of a server. If the backup is not found on the machine just return
|
||||||
|
// a 404 error. The service calling this endpoint can make its own decisions as to how it wants
|
||||||
|
// to handle that response.
|
||||||
func deleteServerBackup(c *gin.Context) {
|
func deleteServerBackup(c *gin.Context) {
|
||||||
s := GetServer(c.Param("server"))
|
s := GetServer(c.Param("server"))
|
||||||
|
|
||||||
b, _, err := backup.LocateLocal(c.Param("backup"))
|
b, _, err := backup.LocateLocal(c.Param("backup"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Just return from the function at this point if the backup was not located.
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
|
||||||
|
"error": "The requested backup was not found on this server.",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
TrackedServerError(err, s).AbortWithServerError(c)
|
TrackedServerError(err, s).AbortWithServerError(c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := b.Remove(); err != nil {
|
if err := b.Remove(); err != nil {
|
||||||
TrackedServerError(err, s).AbortWithServerError(c)
|
// I'm not entirely sure how likely this is to happen, however if we did manage to locate
|
||||||
return
|
// the backup previously and it is now missing when we go to delete, just treat it as having
|
||||||
|
// been successful, rather than returning a 404.
|
||||||
|
if !errors.Is(err, os.ErrNotExist) {
|
||||||
|
TrackedServerError(err, s).AbortWithServerError(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Status(http.StatusNoContent)
|
c.Status(http.StatusNoContent)
|
||||||
|
|||||||
@@ -149,6 +149,13 @@ func putServerRenameFiles(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := g.Wait(); err != nil {
|
if err := g.Wait(); err != nil {
|
||||||
|
if errors.Is(err, os.ErrExist) {
|
||||||
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "Cannot move or rename file, destination already exists.",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
TrackedServerError(err, s).AbortWithServerError(c)
|
TrackedServerError(err, s).AbortWithServerError(c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -287,7 +294,7 @@ func postServerCompressFiles(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !s.Filesystem.HasSpaceAvailable() {
|
if !s.Filesystem.HasSpaceAvailable(true) {
|
||||||
c.AbortWithStatusJSON(http.StatusConflict, gin.H{
|
c.AbortWithStatusJSON(http.StatusConflict, gin.H{
|
||||||
"error": "This server does not have enough available disk space to generate a compressed archive.",
|
"error": "This server does not have enough available disk space to generate a compressed archive.",
|
||||||
})
|
})
|
||||||
@@ -361,7 +368,7 @@ func postServerUploadFiles(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !s.Filesystem.HasSpaceAvailable() {
|
if !s.Filesystem.HasSpaceAvailable(true) {
|
||||||
c.AbortWithStatusJSON(http.StatusConflict, gin.H{
|
c.AbortWithStatusJSON(http.StatusConflict, gin.H{
|
||||||
"error": "This server does not have enough available disk space to accept any file uploads.",
|
"error": "This server does not have enough available disk space to accept any file uploads.",
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ func (h *Handler) HandleInbound(m Message) error {
|
|||||||
// Only send the current disk usage if the server is offline, if docker container is running,
|
// Only send the current disk usage if the server is offline, if docker container is running,
|
||||||
// Environment#EnableResourcePolling() will send this data to all clients.
|
// Environment#EnableResourcePolling() will send this data to all clients.
|
||||||
if state == environment.ProcessOfflineState {
|
if state == environment.ProcessOfflineState {
|
||||||
_ = h.server.Filesystem.HasSpaceAvailable()
|
_ = h.server.Filesystem.HasSpaceAvailable(false)
|
||||||
|
|
||||||
b, _ := json.Marshal(h.server.Proc())
|
b, _ := json.Marshal(h.server.Proc())
|
||||||
h.SendJson(&Message{
|
h.SendJson(&Message{
|
||||||
|
|||||||
@@ -20,13 +20,11 @@ func (s *Server) notifyPanelOfBackup(uuid string, ad *backup.ArchiveDetails, suc
|
|||||||
s.Log().WithFields(log.Fields{
|
s.Log().WithFields(log.Fields{
|
||||||
"backup": uuid,
|
"backup": uuid,
|
||||||
"error": err,
|
"error": err,
|
||||||
}).Error("failed to notify panel of backup status due to internal code error")
|
}).Error("failed to notify panel of backup status due to wings error")
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Log().WithField("backup", uuid).Warn(rerr.String())
|
|
||||||
|
|
||||||
return errors.New(rerr.String())
|
return errors.New(rerr.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +88,7 @@ func (s *Server) Backup(b backup.BackupInterface) error {
|
|||||||
if notifyError := s.notifyPanelOfBackup(b.Identifier(), &backup.ArchiveDetails{}, false); notifyError != nil {
|
if notifyError := s.notifyPanelOfBackup(b.Identifier(), &backup.ArchiveDetails{}, false); notifyError != nil {
|
||||||
s.Log().WithFields(log.Fields{
|
s.Log().WithFields(log.Fields{
|
||||||
"backup": b.Identifier(),
|
"backup": b.Identifier(),
|
||||||
"error": err,
|
"error": notifyError,
|
||||||
}).Warn("failed to notify panel of failed backup state")
|
}).Warn("failed to notify panel of failed backup state")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +100,7 @@ func (s *Server) Backup(b backup.BackupInterface) error {
|
|||||||
"file_size": 0,
|
"file_size": 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
return errors.WithStack(err)
|
return errors.Wrap(err, "error while generating server backup")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to notify the panel about the status of this backup. If for some reason this request
|
// Try to notify the panel about the status of this backup. If for some reason this request
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
gzip "github.com/klauspost/pgzip"
|
gzip "github.com/klauspost/pgzip"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/remeh/sizedwaitgroup"
|
"github.com/remeh/sizedwaitgroup"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
"io"
|
"io"
|
||||||
@@ -25,7 +26,7 @@ type Archive struct {
|
|||||||
func (a *Archive) Create(dst string, ctx context.Context) (os.FileInfo, error) {
|
func (a *Archive) Create(dst string, ctx context.Context) (os.FileInfo, error) {
|
||||||
f, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
f, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
@@ -49,23 +50,17 @@ func (a *Archive) Create(dst string, ctx context.Context) (os.FileInfo, error) {
|
|||||||
// Iterate over all of the files to be included and put them into the archive. This is
|
// Iterate over all of the files to be included and put them into the archive. This is
|
||||||
// done as a concurrent goroutine to speed things along. If an error is encountered at
|
// done as a concurrent goroutine to speed things along. If an error is encountered at
|
||||||
// any step, the entire process is aborted.
|
// any step, the entire process is aborted.
|
||||||
for p, s := range a.Files.All() {
|
for _, p := range a.Files.All() {
|
||||||
if (*s).IsDir() {
|
p := p
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pa := p
|
|
||||||
st := s
|
|
||||||
|
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
wg.Add()
|
wg.Add()
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return ctx.Err()
|
return errors.WithStack(ctx.Err())
|
||||||
default:
|
default:
|
||||||
return a.addToArchive(pa, st, tw)
|
return a.addToArchive(p, tw)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -80,33 +75,48 @@ func (a *Archive) Create(dst string, ctx context.Context) (os.FileInfo, error) {
|
|||||||
log.WithField("location", dst).Warn("failed to delete corrupted backup archive")
|
log.WithField("location", dst).Warn("failed to delete corrupted backup archive")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, err
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
st, err := f.Stat()
|
st, err := f.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return st, nil
|
return st, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a single file to the existing tar archive writer.
|
// Adds a single file to the existing tar archive writer.
|
||||||
func (a *Archive) addToArchive(p string, s *os.FileInfo, w *tar.Writer) error {
|
func (a *Archive) addToArchive(p string, w *tar.Writer) error {
|
||||||
f, err := os.Open(p)
|
f, err := os.Open(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
// If you try to backup something that no longer exists (got deleted somewhere during the process
|
||||||
|
// but not by this process), just skip over it and don't kill the entire backup.
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
st := *s
|
s, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
// Same as above, don't kill the process just because the file no longer exists.
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
header := &tar.Header{
|
header := &tar.Header{
|
||||||
// Trim the long server path from the name of the file so that the resulting
|
// Trim the long server path from the name of the file so that the resulting
|
||||||
// archive is exactly how the user would see it in the panel file manager.
|
// archive is exactly how the user would see it in the panel file manager.
|
||||||
Name: strings.TrimPrefix(p, a.TrimPrefix),
|
Name: strings.TrimPrefix(p, a.TrimPrefix),
|
||||||
Size: st.Size(),
|
Size: s.Size(),
|
||||||
Mode: int64(st.Mode()),
|
Mode: int64(s.Mode()),
|
||||||
ModTime: st.ModTime(),
|
ModTime: s.ModTime(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// These actions must occur sequentially, even if this function is called multiple
|
// These actions must occur sequentially, even if this function is called multiple
|
||||||
@@ -115,12 +125,12 @@ func (a *Archive) addToArchive(p string, s *os.FileInfo, w *tar.Writer) error {
|
|||||||
defer a.Unlock()
|
defer a.Unlock()
|
||||||
|
|
||||||
if err := w.WriteHeader(header); err != nil {
|
if err := w.WriteHeader(header); err != nil {
|
||||||
return err
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := make([]byte, 4*1024)
|
buf := make([]byte, 4*1024)
|
||||||
if _, err := io.CopyBuffer(w, f, buf); err != nil {
|
if _, err := io.CopyBuffer(w, f, buf); err != nil {
|
||||||
return err
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ func LocateLocal(uuid string) (*LocalBackup, os.FileInfo, error) {
|
|||||||
|
|
||||||
st, err := os.Stat(b.Path())
|
st, err := os.Stat(b.Path())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if st.IsDir() {
|
if st.IsDir() {
|
||||||
return nil, nil, errors.New("invalid archive found; is directory")
|
return nil, nil, errors.New("invalid archive, is directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
return b, st, nil
|
return b, st, nil
|
||||||
@@ -48,7 +48,7 @@ func (b *LocalBackup) Generate(included *IncludedFiles, prefix string) (*Archive
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, err := a.Create(b.Path(), context.Background()); err != nil {
|
if _, err := a.Create(b.Path(), context.Background()); err != nil {
|
||||||
return nil, err
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.Details(), nil
|
return b.Details(), nil
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -33,17 +34,17 @@ func (s *S3Backup) Generate(included *IncludedFiles, prefix string) (*ArchiveDet
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, err := a.Create(s.Path(), context.Background()); err != nil {
|
if _, err := a.Create(s.Path(), context.Background()); err != nil {
|
||||||
return nil, err
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rc, err := os.Open(s.Path())
|
rc, err := os.Open(s.Path())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
defer rc.Close()
|
defer rc.Close()
|
||||||
|
|
||||||
if resp, err := s.generateRemoteRequest(rc); err != nil {
|
if resp, err := s.generateRemoteRequest(rc); err != nil {
|
||||||
return nil, err
|
return nil, errors.WithStack(err)
|
||||||
} else {
|
} else {
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,23 @@
|
|||||||
package backup
|
package backup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IncludedFiles struct {
|
type IncludedFiles struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
files map[string]*os.FileInfo
|
files []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pushes an additional file or folder onto the struct.
|
// Pushes an additional file or folder onto the struct.
|
||||||
func (i *IncludedFiles) Push(info *os.FileInfo, p string) {
|
func (i *IncludedFiles) Push(p string) {
|
||||||
i.Lock()
|
i.Lock()
|
||||||
defer i.Unlock()
|
i.files = append(i.files, p) // ~~
|
||||||
|
i.Unlock()
|
||||||
if i.files == nil {
|
|
||||||
i.files = make(map[string]*os.FileInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
i.files[p] = info
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns all of the files that were marked as being included.
|
// Returns all of the files that were marked as being included.
|
||||||
func (i *IncludedFiles) All() map[string]*os.FileInfo {
|
func (i *IncludedFiles) All() []string {
|
||||||
i.RLock()
|
i.RLock()
|
||||||
defer i.RUnlock()
|
defer i.RUnlock()
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ type Configuration struct {
|
|||||||
// The command that should be used when booting up the server instance.
|
// The command that should be used when booting up the server instance.
|
||||||
Invocation string `json:"invocation"`
|
Invocation string `json:"invocation"`
|
||||||
|
|
||||||
|
// By default this is false, however if selected within the Panel while installing or re-installing a
|
||||||
|
// server, specific installation scripts will be skipped for the server process.
|
||||||
|
SkipEggScripts bool `default:"false" json:"skip_egg_scripts"`
|
||||||
|
|
||||||
// An array of environment variables that should be passed along to the running
|
// An array of environment variables that should be passed along to the running
|
||||||
// server process.
|
// server process.
|
||||||
EnvVars environment.Variables `json:"environment"`
|
EnvVars environment.Variables `json:"environment"`
|
||||||
@@ -43,6 +47,20 @@ func (s *Server) Config() *Configuration {
|
|||||||
return &s.cfg
|
return &s.cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) DiskSpace() int64 {
|
||||||
|
s.cfg.mu.RLock()
|
||||||
|
defer s.cfg.mu.RUnlock()
|
||||||
|
|
||||||
|
return s.cfg.Build.DiskSpace
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) MemoryLimit() int64 {
|
||||||
|
s.cfg.mu.RLock()
|
||||||
|
defer s.cfg.mu.RUnlock()
|
||||||
|
|
||||||
|
return s.cfg.Build.MemoryLimit
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Configuration) GetUuid() string {
|
func (c *Configuration) GetUuid() string {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
defer c.mu.RUnlock()
|
defer c.mu.RUnlock()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gabriel-vasile/mimetype"
|
"github.com/gabriel-vasile/mimetype"
|
||||||
|
"github.com/karrick/godirwalk"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/server/backup"
|
"github.com/pterodactyl/wings/server/backup"
|
||||||
@@ -21,6 +22,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,10 +41,11 @@ func IsPathResolutionError(err error) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Filesystem struct {
|
type Filesystem struct {
|
||||||
mu sync.RWMutex
|
mu sync.Mutex
|
||||||
|
|
||||||
lastLookupTime time.Time
|
lastLookupTime time.Time
|
||||||
diskUsage int64
|
lookupInProgress int32
|
||||||
|
disk int64
|
||||||
|
|
||||||
Server *Server
|
Server *Server
|
||||||
}
|
}
|
||||||
@@ -204,15 +207,20 @@ func (fs *Filesystem) ParallelSafePath(paths []string) ([]string, error) {
|
|||||||
return cleaned, g.Wait()
|
return cleaned, g.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SpaceCheckingOpts struct {
|
||||||
|
AllowStaleResponse bool
|
||||||
|
}
|
||||||
|
|
||||||
// Determines if the directory a file is trying to be added to has enough space available
|
// Determines if the directory a file is trying to be added to has enough space available
|
||||||
// for the file to be written to.
|
// for the file to be written to.
|
||||||
//
|
//
|
||||||
// Because determining the amount of space being used by a server is a taxing operation we
|
// Because determining the amount of space being used by a server is a taxing operation we
|
||||||
// will load it all up into a cache and pull from that as long as the key is not expired.
|
// will load it all up into a cache and pull from that as long as the key is not expired.
|
||||||
func (fs *Filesystem) HasSpaceAvailable() bool {
|
//
|
||||||
space := fs.Server.Build().DiskSpace
|
// This operation will potentially block unless allowStaleValue is set to true. See the
|
||||||
|
// documentation on DiskUsage for how this affects the call.
|
||||||
size, err := fs.getCachedDiskUsage()
|
func (fs *Filesystem) HasSpaceAvailable(allowStaleValue bool) bool {
|
||||||
|
size, err := fs.DiskUsage(allowStaleValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fs.Server.Log().WithField("error", err).Warn("failed to determine root server directory size")
|
fs.Server.Log().WithField("error", err).Warn("failed to determine root server directory size")
|
||||||
}
|
}
|
||||||
@@ -221,6 +229,7 @@ func (fs *Filesystem) HasSpaceAvailable() bool {
|
|||||||
// been allocated.
|
// been allocated.
|
||||||
fs.Server.Proc().SetDisk(size)
|
fs.Server.Proc().SetDisk(size)
|
||||||
|
|
||||||
|
space := fs.Server.DiskSpace()
|
||||||
// If space is -1 or 0 just return true, means they're allowed unlimited.
|
// If space is -1 or 0 just return true, means they're allowed unlimited.
|
||||||
//
|
//
|
||||||
// Technically we could skip disk space calculation because we don't need to check if the server exceeds it's limit
|
// Technically we could skip disk space calculation because we don't need to check if the server exceeds it's limit
|
||||||
@@ -237,19 +246,44 @@ func (fs *Filesystem) HasSpaceAvailable() bool {
|
|||||||
// as needed without overly taxing the system. This will prioritize the value from the cache to avoid
|
// as needed without overly taxing the system. This will prioritize the value from the cache to avoid
|
||||||
// excessive IO usage. We will only walk the filesystem and determine the size of the directory if there
|
// excessive IO usage. We will only walk the filesystem and determine the size of the directory if there
|
||||||
// is no longer a cached value.
|
// is no longer a cached value.
|
||||||
func (fs *Filesystem) getCachedDiskUsage() (int64, error) {
|
//
|
||||||
|
// If "allowStaleValue" is set to true, a stale value MAY be returned to the caller if there is an
|
||||||
|
// expired cache value AND there is currently another lookup in progress. If there is no cached value but
|
||||||
|
// no other lookup is in progress, a fresh disk space response will be returned to the caller.
|
||||||
|
//
|
||||||
|
// This is primarily to avoid a bunch of I/O operations from piling up on the server, especially on servers
|
||||||
|
// with a large amount of files.
|
||||||
|
func (fs *Filesystem) DiskUsage(allowStaleValue bool) (int64, error) {
|
||||||
|
// Check if cache is expired...
|
||||||
|
if !fs.lastLookupTime.After(time.Now().Add(time.Second * -150)) {
|
||||||
|
// If we are now allowing a stale response, or there is no lookup currently in progress, go ahead
|
||||||
|
// and perform the lookup and return the fresh value. This is a blocking operation to the calling
|
||||||
|
// process.
|
||||||
|
if !allowStaleValue || atomic.LoadInt32(&fs.lookupInProgress) == 0 {
|
||||||
|
return fs.updateCachedDiskUsage()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, just go ahead and perform the cached disk usage update.
|
||||||
|
go fs.updateCachedDiskUsage()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the currently cached value back to the calling function.
|
||||||
|
return atomic.LoadInt64(&fs.disk), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates the currently used disk space for a server.
|
||||||
|
func (fs *Filesystem) updateCachedDiskUsage() (int64, error) {
|
||||||
// Obtain an exclusive lock on this process so that we don't unintentionally run it at the same
|
// Obtain an exclusive lock on this process so that we don't unintentionally run it at the same
|
||||||
// time as another running process. Once the lock is available it'll read from the cache for the
|
// time as another running process. Once the lock is available it'll read from the cache for the
|
||||||
// second call rather than hitting the disk in parallel.
|
// second call rather than hitting the disk in parallel.
|
||||||
//
|
|
||||||
// This effectively the same speed as running this call in parallel since this cache will return
|
|
||||||
// instantly on the second call.
|
|
||||||
fs.mu.Lock()
|
fs.mu.Lock()
|
||||||
defer fs.mu.Unlock()
|
defer fs.mu.Unlock()
|
||||||
|
|
||||||
if fs.lastLookupTime.After(time.Now().Add(time.Second * -60)) {
|
// Always clear the in progress flag when this process finishes.
|
||||||
return fs.diskUsage, nil
|
defer atomic.StoreInt32(&fs.lookupInProgress, 0)
|
||||||
}
|
|
||||||
|
// Signal that we're currently updating the disk size, to prevent other routines to block on this.
|
||||||
|
atomic.StoreInt32(&fs.lookupInProgress, 1)
|
||||||
|
|
||||||
// If there is no size its either because there is no data (in which case running this function
|
// If there is no size its either because there is no data (in which case running this function
|
||||||
// will have effectively no impact), or there is nothing in the cache, in which case we need to
|
// will have effectively no impact), or there is nothing in the cache, in which case we need to
|
||||||
@@ -261,7 +295,7 @@ func (fs *Filesystem) getCachedDiskUsage() (int64, error) {
|
|||||||
// so that we don't cause an endless loop of determining the disk size if there is a temporary
|
// so that we don't cause an endless loop of determining the disk size if there is a temporary
|
||||||
// error encountered.
|
// error encountered.
|
||||||
fs.lastLookupTime = time.Now()
|
fs.lastLookupTime = time.Now()
|
||||||
atomic.StoreInt64(&fs.diskUsage, size)
|
atomic.StoreInt64(&fs.disk, size)
|
||||||
|
|
||||||
return size, err
|
return size, err
|
||||||
}
|
}
|
||||||
@@ -270,20 +304,40 @@ func (fs *Filesystem) getCachedDiskUsage() (int64, error) {
|
|||||||
// through all of the folders. Returns the size in bytes. This can be a fairly taxing operation
|
// through all of the folders. Returns the size in bytes. This can be a fairly taxing operation
|
||||||
// on locations with tons of files, so it is recommended that you cache the output.
|
// on locations with tons of files, so it is recommended that you cache the output.
|
||||||
func (fs *Filesystem) DirectorySize(dir string) (int64, error) {
|
func (fs *Filesystem) DirectorySize(dir string) (int64, error) {
|
||||||
|
d, err := fs.SafePath(dir)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
var size int64
|
var size int64
|
||||||
err := fs.Walk(dir, func(_ string, f os.FileInfo, err error) error {
|
var st syscall.Stat_t
|
||||||
if err != nil {
|
|
||||||
return fs.handleWalkerError(err, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !f.IsDir() {
|
err = godirwalk.Walk(d, &godirwalk.Options{
|
||||||
atomic.AddInt64(&size, f.Size())
|
Unsorted: true,
|
||||||
}
|
Callback: func(p string, e *godirwalk.Dirent) error {
|
||||||
|
// If this is a symlink then resolve the final destination of it before trying to continue walking
|
||||||
|
// over its contents. If it resolves outside the server data directory just skip everything else for
|
||||||
|
// it. Otherwise, allow it to continue.
|
||||||
|
if e.IsSymlink() {
|
||||||
|
if _, err := fs.SafePath(p); err != nil {
|
||||||
|
if IsPathResolutionError(err) {
|
||||||
|
return godirwalk.SkipThis
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !e.IsDir() {
|
||||||
|
syscall.Lstat(p, &st)
|
||||||
|
atomic.AddInt64(&size, st.Size)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return size, err
|
return size, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reads a file on the system and returns it as a byte representation in a file
|
// Reads a file on the system and returns it as a byte representation in a file
|
||||||
@@ -346,7 +400,7 @@ func (fs *Filesystem) Writefile(p string, r io.Reader) error {
|
|||||||
sz, err := io.CopyBuffer(file, r, buf)
|
sz, err := io.CopyBuffer(file, r, buf)
|
||||||
|
|
||||||
// Adjust the disk usage to account for the old size and the new size of the file.
|
// Adjust the disk usage to account for the old size and the new size of the file.
|
||||||
atomic.AddInt64(&fs.diskUsage, sz-currentSize)
|
atomic.AddInt64(&fs.disk, sz-currentSize)
|
||||||
|
|
||||||
// Finally, chown the file to ensure the permissions don't end up out-of-whack
|
// Finally, chown the file to ensure the permissions don't end up out-of-whack
|
||||||
// if we had just created it.
|
// if we had just created it.
|
||||||
@@ -442,17 +496,22 @@ func (fs *Filesystem) Rename(from string, to string) error {
|
|||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if f, err := os.Stat(cleanedFrom); err != nil {
|
// If the target file or directory already exists the rename function will fail, so just
|
||||||
return errors.WithStack(err)
|
// bail out now.
|
||||||
} else {
|
if _, err := os.Stat(cleanedTo); err == nil {
|
||||||
d := cleanedTo
|
return os.ErrExist
|
||||||
if !f.IsDir() {
|
}
|
||||||
d = strings.TrimSuffix(d, path.Base(cleanedTo))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that the directory we're moving into exists correctly on the system.
|
if cleanedTo == fs.Path() {
|
||||||
|
return errors.New("attempting to rename into an invalid directory space")
|
||||||
|
}
|
||||||
|
|
||||||
|
d := strings.TrimSuffix(cleanedTo, path.Base(cleanedTo))
|
||||||
|
// Ensure that the directory we're moving into exists correctly on the system. Only do this if
|
||||||
|
// we're not at the root directory level.
|
||||||
|
if d != fs.Path() {
|
||||||
if mkerr := os.MkdirAll(d, 0644); mkerr != nil {
|
if mkerr := os.MkdirAll(d, 0644); mkerr != nil {
|
||||||
return errors.WithStack(mkerr)
|
return errors.Wrap(mkerr, "failed to create directory structure for file rename")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -485,19 +544,22 @@ func (fs *Filesystem) Chown(path string) error {
|
|||||||
|
|
||||||
// If this was a directory, begin walking over its contents recursively and ensure that all
|
// If this was a directory, begin walking over its contents recursively and ensure that all
|
||||||
// of the subfiles and directories get their permissions updated as well.
|
// of the subfiles and directories get their permissions updated as well.
|
||||||
return fs.Walk(cleaned, func(path string, f os.FileInfo, err error) error {
|
return godirwalk.Walk(cleaned, &godirwalk.Options{
|
||||||
if err != nil {
|
Unsorted: true,
|
||||||
return fs.handleWalkerError(err, f)
|
Callback: func(p string, e *godirwalk.Dirent) error {
|
||||||
}
|
// Do not attempt to chmod a symlink. Go's os.Chown function will affect the symlink
|
||||||
|
// so if it points to a location outside the data directory the user would be able to
|
||||||
|
// (un)intentionally modify that files permissions.
|
||||||
|
if e.IsSymlink() {
|
||||||
|
if e.IsDir() {
|
||||||
|
return godirwalk.SkipThis
|
||||||
|
}
|
||||||
|
|
||||||
// Do not attempt to chmod a symlink. Go's os.Chown function will affect the symlink
|
return nil
|
||||||
// so if it points to a location outside the data directory the user would be able to
|
}
|
||||||
// (un)intentionally modify that files permissions.
|
|
||||||
if f.Mode()&os.ModeSymlink != 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return os.Chown(path, uid, gid)
|
return os.Chown(p, uid, gid)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -574,7 +636,8 @@ func (fs *Filesystem) Copy(p string) error {
|
|||||||
}
|
}
|
||||||
defer dest.Close()
|
defer dest.Close()
|
||||||
|
|
||||||
if _, err := io.Copy(dest, source); err != nil {
|
buf := make([]byte, 1024*4)
|
||||||
|
if _, err := io.CopyBuffer(dest, source, buf); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -609,11 +672,11 @@ func (fs *Filesystem) Delete(p string) error {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !st.IsDir() {
|
if !st.IsDir() {
|
||||||
atomic.SwapInt64(&fs.diskUsage, -st.Size())
|
atomic.SwapInt64(&fs.disk, -st.Size())
|
||||||
} else {
|
} else {
|
||||||
go func(st os.FileInfo, resolved string) {
|
go func(st os.FileInfo, resolved string) {
|
||||||
if s, err := fs.DirectorySize(resolved); err == nil {
|
if s, err := fs.DirectorySize(resolved); err == nil {
|
||||||
atomic.AddInt64(&fs.diskUsage, -s)
|
atomic.AddInt64(&fs.disk, -s)
|
||||||
}
|
}
|
||||||
}(st, resolved)
|
}(st, resolved)
|
||||||
}
|
}
|
||||||
@@ -732,26 +795,38 @@ func (fs *Filesystem) GetIncludedFiles(dir string, ignored []string) (*backup.In
|
|||||||
// files found, and will keep walking deeper and deeper into directories.
|
// files found, and will keep walking deeper and deeper into directories.
|
||||||
inc := new(backup.IncludedFiles)
|
inc := new(backup.IncludedFiles)
|
||||||
|
|
||||||
if err := fs.Walk(cleaned, func(p string, f os.FileInfo, err error) error {
|
err = godirwalk.Walk(cleaned, &godirwalk.Options{
|
||||||
if err != nil {
|
Unsorted: true,
|
||||||
return fs.handleWalkerError(err, f)
|
Callback: func(p string, e *godirwalk.Dirent) error {
|
||||||
}
|
sp := p
|
||||||
|
if e.IsSymlink() {
|
||||||
|
sp, err = fs.SafePath(p)
|
||||||
|
if err != nil {
|
||||||
|
if IsPathResolutionError(err) {
|
||||||
|
return godirwalk.SkipThis
|
||||||
|
}
|
||||||
|
|
||||||
// Avoid unnecessary parsing if there are no ignored files, nothing will match anyways
|
return err
|
||||||
// so no reason to call the function.
|
}
|
||||||
if len(ignored) == 0 || !i.MatchesPath(strings.TrimPrefix(p, fs.Path()+"/")) {
|
}
|
||||||
inc.Push(&f, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can't just abort if the path is technically ignored. It is possible there is a nested
|
// Only push files into the result array since archives can't create an empty directory within them.
|
||||||
// file or folder that should not be excluded, so in this case we need to just keep going
|
if !e.IsDir() {
|
||||||
// until we get to a final state.
|
// Avoid unnecessary parsing if there are no ignored files, nothing will match anyways
|
||||||
return nil
|
// so no reason to call the function.
|
||||||
}); err != nil {
|
if len(ignored) == 0 || !i.MatchesPath(strings.TrimPrefix(sp, fs.Path()+"/")) {
|
||||||
return nil, err
|
inc.Push(sp)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return inc, nil
|
// We can't just abort if the path is technically ignored. It is possible there is a nested
|
||||||
|
// file or folder that should not be excluded, so in this case we need to just keep going
|
||||||
|
// until we get to a final state.
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return inc, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compresses all of the files matching the given paths in the specified directory. This function
|
// Compresses all of the files matching the given paths in the specified directory. This function
|
||||||
@@ -788,24 +863,38 @@ func (fs *Filesystem) CompressFiles(dir string, paths []string) (os.FileInfo, er
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.IsDir() {
|
if !f.IsDir() {
|
||||||
err := fs.Walk(p, func(s string, info os.FileInfo, err error) error {
|
inc.Push(p)
|
||||||
if err != nil {
|
} else {
|
||||||
return fs.handleWalkerError(err, info)
|
err := godirwalk.Walk(p, &godirwalk.Options{
|
||||||
}
|
Unsorted: true,
|
||||||
|
Callback: func(p string, e *godirwalk.Dirent) error {
|
||||||
|
sp := p
|
||||||
|
if e.IsSymlink() {
|
||||||
|
// Ensure that any symlinks are properly resolved to their final destination. If
|
||||||
|
// that destination is outside the server directory skip over this entire item, otherwise
|
||||||
|
// use the resolved location for the rest of this function.
|
||||||
|
sp, err = fs.SafePath(p)
|
||||||
|
if err != nil {
|
||||||
|
if IsPathResolutionError(err) {
|
||||||
|
return godirwalk.SkipThis
|
||||||
|
}
|
||||||
|
|
||||||
if !info.IsDir() {
|
return err
|
||||||
inc.Push(&info, s)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if !e.IsDir() {
|
||||||
|
inc.Push(sp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
inc.Push(&f, p)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,7 +18,7 @@ import (
|
|||||||
func (fs *Filesystem) SpaceAvailableForDecompression(dir string, file string) (bool, error) {
|
func (fs *Filesystem) SpaceAvailableForDecompression(dir string, file string) (bool, error) {
|
||||||
// Don't waste time trying to determine this if we know the server will have the space for
|
// Don't waste time trying to determine this if we know the server will have the space for
|
||||||
// it since there is no limit.
|
// it since there is no limit.
|
||||||
if fs.Server.Build().DiskSpace <= 0 {
|
if fs.Server.DiskSpace() <= 0 {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,37 +27,19 @@ func (fs *Filesystem) SpaceAvailableForDecompression(dir string, file string) (b
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
wg := new(sync.WaitGroup)
|
|
||||||
|
|
||||||
var dirSize int64
|
|
||||||
var cErr error
|
|
||||||
// Get the cached size in a parallel process so that if it is not cached we are not
|
// Get the cached size in a parallel process so that if it is not cached we are not
|
||||||
// waiting an unnecessary amount of time on this call.
|
// waiting an unnecessary amount of time on this call.
|
||||||
go func() {
|
dirSize, err := fs.DiskUsage(false)
|
||||||
wg.Add(1)
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
dirSize, cErr = fs.getCachedDiskUsage()
|
|
||||||
}()
|
|
||||||
|
|
||||||
var size int64
|
var size int64
|
||||||
// In a seperate thread, walk over the archive and figure out just how large the final
|
// Walk over the archive and figure out just how large the final output would be from unarchiving it.
|
||||||
// output would be from dearchiving it.
|
archiver.Walk(source, func(f archiver.File) error {
|
||||||
go func() {
|
atomic.AddInt64(&size, f.Size())
|
||||||
wg.Add(1)
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
// Walk all of the files and calculate the total decompressed size of this archive.
|
return nil
|
||||||
archiver.Walk(source, func(f archiver.File) error {
|
})
|
||||||
atomic.AddInt64(&size, f.Size())
|
|
||||||
|
|
||||||
return nil
|
return ((dirSize + size) / 1000.0 / 1000.0) <= fs.Server.DiskSpace(), errors.WithStack(err)
|
||||||
})
|
|
||||||
}()
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
return ((dirSize + size) / 1000.0 / 1000.0) <= fs.Server.Build().DiskSpace, cErr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decompress a file in a given directory by using the archiver tool to infer the file
|
// Decompress a file in a given directory by using the archiver tool to infer the file
|
||||||
|
|||||||
@@ -1,141 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"github.com/gammazero/workerpool"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FileWalker struct {
|
|
||||||
*Filesystem
|
|
||||||
}
|
|
||||||
|
|
||||||
type PooledFileWalker struct {
|
|
||||||
wg sync.WaitGroup
|
|
||||||
pool *workerpool.WorkerPool
|
|
||||||
callback filepath.WalkFunc
|
|
||||||
cancel context.CancelFunc
|
|
||||||
|
|
||||||
err error
|
|
||||||
errOnce sync.Once
|
|
||||||
|
|
||||||
Filesystem *Filesystem
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a new walker instance.
|
|
||||||
func (fs *Filesystem) NewWalker() *FileWalker {
|
|
||||||
return &FileWalker{fs}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new pooled file walker that will concurrently walk over a given directory but limit itself
|
|
||||||
// to a worker pool as to not completely flood out the system or cause a process crash.
|
|
||||||
func newPooledWalker(fs *Filesystem) *PooledFileWalker {
|
|
||||||
return &PooledFileWalker{
|
|
||||||
Filesystem: fs,
|
|
||||||
// Create a worker pool that is the same size as the number of processors available on the
|
|
||||||
// system. Going much higher doesn't provide much of a performance boost, and is only more
|
|
||||||
// likely to lead to resource overloading anyways.
|
|
||||||
pool: workerpool.New(runtime.NumCPU()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process a given path by calling the callback function for all of the files and directories within
|
|
||||||
// the path, and then dropping into any directories that we come across.
|
|
||||||
func (w *PooledFileWalker) process(path string) error {
|
|
||||||
p, err := w.Filesystem.SafePath(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
files, err := ioutil.ReadDir(p)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop over all of the files and directories in the given directory and call the provided
|
|
||||||
// callback function. If we encounter a directory, push that directory onto the worker queue
|
|
||||||
// to be processed.
|
|
||||||
for _, f := range files {
|
|
||||||
sp, err := w.Filesystem.SafeJoin(p, f)
|
|
||||||
if err != nil {
|
|
||||||
// Let the callback function handle what to do if there is a path resolution error because a
|
|
||||||
// dangerous path was resolved. If there is an error returned, return from this entire process
|
|
||||||
// otherwise just skip over this specific file. We don't care if its a file or a directory at
|
|
||||||
// this point since either way we're skipping it, however, still check for the SkipDir since that
|
|
||||||
// would be thrown otherwise.
|
|
||||||
if err = w.callback(sp, f, err); err != nil && err != filepath.SkipDir {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
i, err := os.Stat(sp)
|
|
||||||
// You might end up getting an error about a file or folder not existing if the given path
|
|
||||||
// if it is an invalid symlink. We can safely just skip over these files I believe.
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call the user-provided callback for this file or directory. If an error is returned that is
|
|
||||||
// not a SkipDir call, abort the entire process and bubble that error up.
|
|
||||||
if err = w.callback(sp, i, err); err != nil && err != filepath.SkipDir {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is a directory, and we didn't get a SkipDir error, continue through by pushing another
|
|
||||||
// job to the pool to handle it. If we requested a skip, don't do anything just continue on to the
|
|
||||||
// next item.
|
|
||||||
if i.IsDir() && err != filepath.SkipDir {
|
|
||||||
w.push(sp)
|
|
||||||
} else if !i.IsDir() && err == filepath.SkipDir {
|
|
||||||
// Per the spec for the callback, if we get a SkipDir error but it is returned for an item
|
|
||||||
// that is _not_ a directory, abort the remaining operations on the directory.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push a new path into the worker pool and increment the waitgroup so that we do not return too
|
|
||||||
// early and cause panic's as internal directories attempt to submit to the pool.
|
|
||||||
func (w *PooledFileWalker) push(path string) {
|
|
||||||
w.wg.Add(1)
|
|
||||||
w.pool.Submit(func() {
|
|
||||||
defer w.wg.Done()
|
|
||||||
if err := w.process(path); err != nil {
|
|
||||||
w.errOnce.Do(func() {
|
|
||||||
w.err = err
|
|
||||||
if w.cancel != nil {
|
|
||||||
w.cancel()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walks the given directory and executes the callback function for all of the files and directories
|
|
||||||
// that are encountered.
|
|
||||||
func (fs *Filesystem) Walk(dir string, callback filepath.WalkFunc) error {
|
|
||||||
w := newPooledWalker(fs)
|
|
||||||
w.callback = callback
|
|
||||||
|
|
||||||
_, cancel := context.WithCancel(context.Background())
|
|
||||||
w.cancel = cancel
|
|
||||||
|
|
||||||
w.push(dir)
|
|
||||||
|
|
||||||
w.wg.Wait()
|
|
||||||
w.pool.StopWait()
|
|
||||||
|
|
||||||
if w.err != nil {
|
|
||||||
return w.err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -35,10 +35,17 @@ func (s *Server) Install(sync bool) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the start event so the Panel can automatically update.
|
var err error
|
||||||
s.Events().Publish(InstallStartedEvent, "")
|
if !s.Config().SkipEggScripts {
|
||||||
|
// Send the start event so the Panel can automatically update. We don't send this unless the process
|
||||||
|
// is actually going to run, otherwise all sorts of weird rapid UI behavior happens since there isn't
|
||||||
|
// an actual install process being executed.
|
||||||
|
s.Events().Publish(InstallStartedEvent, "")
|
||||||
|
|
||||||
err := s.internalInstall()
|
err = s.internalInstall()
|
||||||
|
} else {
|
||||||
|
s.Log().Info("server configured to skip running installation scripts for this egg, not executing process")
|
||||||
|
}
|
||||||
|
|
||||||
s.Log().Debug("notifying panel of server install state")
|
s.Log().Debug("notifying panel of server install state")
|
||||||
if serr := s.SyncInstallState(err == nil); serr != nil {
|
if serr := s.SyncInstallState(err == nil); serr != nil {
|
||||||
|
|||||||
@@ -46,10 +46,7 @@ func (s *Server) StartEventListeners() {
|
|||||||
s.resources.Stats = *st
|
s.resources.Stats = *st
|
||||||
s.resources.mu.Unlock()
|
s.resources.mu.Unlock()
|
||||||
|
|
||||||
// TODO: we'll need to handle this better since calling it in rapid succession will
|
s.Filesystem.HasSpaceAvailable(true)
|
||||||
// cause it to block until the first call is done calculating disk usage, which will
|
|
||||||
// case stat events to pile up for the server.
|
|
||||||
s.Filesystem.HasSpaceAvailable()
|
|
||||||
|
|
||||||
s.emitProcUsage()
|
s.emitProcUsage()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ func FromConfiguration(data *api.ServerConfigurationResponse) (*Server, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.cfg = cfg
|
s.cfg = cfg
|
||||||
if err := s.UpdateDataStructure(data.Settings, false); err != nil {
|
if err := s.UpdateDataStructure(data.Settings); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,10 +103,15 @@ func FromConfiguration(data *api.ServerConfigurationResponse) (*Server, error) {
|
|||||||
// Right now we only support a Docker based environment, so I'm going to hard code
|
// Right now we only support a Docker based environment, so I'm going to hard code
|
||||||
// this logic in. When we're ready to support other environment we'll need to make
|
// this logic in. When we're ready to support other environment we'll need to make
|
||||||
// some modifications here obviously.
|
// some modifications here obviously.
|
||||||
envCfg := environment.NewConfiguration(s.Mounts(), s.cfg.Allocations, s.cfg.Build, s.cfg.EnvVars)
|
settings := environment.Settings{
|
||||||
|
Mounts: s.Mounts(),
|
||||||
|
Allocations: s.cfg.Allocations,
|
||||||
|
Limits: s.cfg.Build,
|
||||||
|
}
|
||||||
|
|
||||||
|
envCfg := environment.NewConfiguration(settings, s.GetEnvironmentVariables())
|
||||||
meta := docker.Metadata{
|
meta := docker.Metadata{
|
||||||
Invocation: s.Config().Invocation,
|
Image: s.Config().Container.Image,
|
||||||
Image: s.Config().Container.Image,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if env, err := docker.New(s.Id(), &meta, envCfg); err != nil {
|
if env, err := docker.New(s.Id(), &meta, envCfg); err != nil {
|
||||||
@@ -123,7 +128,7 @@ func FromConfiguration(data *api.ServerConfigurationResponse) (*Server, error) {
|
|||||||
|
|
||||||
// If the server's data directory exists, force disk usage calculation.
|
// If the server's data directory exists, force disk usage calculation.
|
||||||
if _, err := os.Stat(s.Filesystem.Path()); err == nil {
|
if _, err := os.Stat(s.Filesystem.Path()); err == nil {
|
||||||
go s.Filesystem.HasSpaceAvailable()
|
s.Filesystem.HasSpaceAvailable(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package server
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/pterodactyl/wings/config"
|
||||||
"golang.org/x/sync/semaphore"
|
"golang.org/x/sync/semaphore"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
@@ -125,17 +126,23 @@ func (s *Server) HandlePowerAction(action PowerAction, waitSeconds ...int) error
|
|||||||
// Execute a few functions before actually calling the environment start commands. This ensures
|
// Execute a few functions before actually calling the environment start commands. This ensures
|
||||||
// that everything is ready to go for environment booting, and that the server can even be started.
|
// that everything is ready to go for environment booting, and that the server can even be started.
|
||||||
func (s *Server) onBeforeStart() error {
|
func (s *Server) onBeforeStart() error {
|
||||||
// Disallow start & restart if the server is suspended.
|
|
||||||
if s.IsSuspended() {
|
|
||||||
return new(suspendedError)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Log().Info("syncing server configuration with panel")
|
s.Log().Info("syncing server configuration with panel")
|
||||||
if err := s.Sync(); err != nil {
|
if err := s.Sync(); err != nil {
|
||||||
return errors.Wrap(err, "unable to sync server data from Panel instance")
|
return errors.Wrap(err, "unable to sync server data from Panel instance")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !s.Filesystem.HasSpaceAvailable() {
|
// Disallow start & restart if the server is suspended. Do this check after performing a sync
|
||||||
|
// action with the Panel to ensure that we have the most up-to-date information for that server.
|
||||||
|
if s.IsSuspended() {
|
||||||
|
return new(suspendedError)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we sync the server information with the environment so that any new environment variables
|
||||||
|
// and process resource limits are correctly applied.
|
||||||
|
s.SyncWithEnvironment()
|
||||||
|
|
||||||
|
s.PublishConsoleOutputFromDaemon("Checking server disk space usage, this could take a few seconds...")
|
||||||
|
if !s.Filesystem.HasSpaceAvailable(false) {
|
||||||
return errors.New("cannot start server, not enough disk space available")
|
return errors.New("cannot start server, not enough disk space available")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,10 +154,12 @@ func (s *Server) onBeforeStart() error {
|
|||||||
s.PublishConsoleOutputFromDaemon("Updating process configuration files...")
|
s.PublishConsoleOutputFromDaemon("Updating process configuration files...")
|
||||||
s.UpdateConfigurationFiles()
|
s.UpdateConfigurationFiles()
|
||||||
|
|
||||||
s.PublishConsoleOutputFromDaemon("Ensuring file permissions are set correctly, this could take a few seconds...")
|
if config.Get().System.CheckPermissionsOnBoot {
|
||||||
// Ensure all of the server file permissions are set correctly before booting the process.
|
s.PublishConsoleOutputFromDaemon("Ensuring file permissions are set correctly, this could take a few seconds...")
|
||||||
if err := s.Filesystem.Chown("/"); err != nil {
|
// Ensure all of the server file permissions are set correctly before booting the process.
|
||||||
return errors.Wrap(err, "failed to chown root server directory during pre-boot process")
|
if err := s.Filesystem.Chown("/"); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to chown root server directory during pre-boot process")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ func (s *Server) GetEnvironmentVariables() []string {
|
|||||||
var out = []string{
|
var out = []string{
|
||||||
fmt.Sprintf("TZ=%s", zone),
|
fmt.Sprintf("TZ=%s", zone),
|
||||||
fmt.Sprintf("STARTUP=%s", s.Config().Invocation),
|
fmt.Sprintf("STARTUP=%s", s.Config().Invocation),
|
||||||
fmt.Sprintf("SERVER_MEMORY=%d", s.Build().MemoryLimit),
|
fmt.Sprintf("SERVER_MEMORY=%d", s.MemoryLimit()),
|
||||||
fmt.Sprintf("SERVER_IP=%s", s.Config().Allocations.DefaultMapping.Ip),
|
fmt.Sprintf("SERVER_IP=%s", s.Config().Allocations.DefaultMapping.Ip),
|
||||||
fmt.Sprintf("SERVER_PORT=%d", s.Config().Allocations.DefaultMapping.Port),
|
fmt.Sprintf("SERVER_PORT=%d", s.Config().Allocations.DefaultMapping.Port),
|
||||||
}
|
}
|
||||||
@@ -125,7 +125,7 @@ func (s *Server) Sync() error {
|
|||||||
|
|
||||||
func (s *Server) SyncWithConfiguration(cfg *api.ServerConfigurationResponse) error {
|
func (s *Server) SyncWithConfiguration(cfg *api.ServerConfigurationResponse) error {
|
||||||
// Update the data structure and persist it to the disk.
|
// Update the data structure and persist it to the disk.
|
||||||
if err := s.UpdateDataStructure(cfg.Settings, false); err != nil {
|
if err := s.UpdateDataStructure(cfg.Settings); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,10 +177,6 @@ func (s *Server) IsSuspended() bool {
|
|||||||
return s.Config().Suspended
|
return s.Config().Suspended
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Build() *environment.Limits {
|
|
||||||
return &s.Config().Build
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) ProcessConfiguration() *api.ProcessConfiguration {
|
func (s *Server) ProcessConfiguration() *api.ProcessConfiguration {
|
||||||
s.RLock()
|
s.RLock()
|
||||||
defer s.RUnlock()
|
defer s.RUnlock()
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
// The server will be marked as requiring a rebuild on the next boot sequence,
|
// The server will be marked as requiring a rebuild on the next boot sequence,
|
||||||
// it is up to the specific environment to determine what needs to happen when
|
// it is up to the specific environment to determine what needs to happen when
|
||||||
// that is the case.
|
// that is the case.
|
||||||
func (s *Server) UpdateDataStructure(data []byte, background bool) error {
|
func (s *Server) UpdateDataStructure(data []byte) error {
|
||||||
src := new(Configuration)
|
src := new(Configuration)
|
||||||
if err := json.Unmarshal(data, src); err != nil {
|
if err := json.Unmarshal(data, src); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
@@ -80,6 +80,14 @@ func (s *Server) UpdateDataStructure(data []byte, background bool) error {
|
|||||||
c.Suspended = v
|
c.Suspended = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, err := jsonparser.GetBoolean(data, "skip_egg_scripts"); err != nil {
|
||||||
|
if err != jsonparser.KeyPathNotFoundError {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.SkipEggScripts = v
|
||||||
|
}
|
||||||
|
|
||||||
// Environment and Mappings should be treated as a full update at all times, never a
|
// Environment and Mappings should be treated as a full update at all times, never a
|
||||||
// true patch, otherwise we can't know what we're passing along.
|
// true patch, otherwise we can't know what we're passing along.
|
||||||
if src.EnvVars != nil && len(src.EnvVars) > 0 {
|
if src.EnvVars != nil && len(src.EnvVars) > 0 {
|
||||||
@@ -97,36 +105,50 @@ func (s *Server) UpdateDataStructure(data []byte, background bool) error {
|
|||||||
// Update the configuration once we have a lock on the configuration object.
|
// Update the configuration once we have a lock on the configuration object.
|
||||||
s.cfg = c
|
s.cfg = c
|
||||||
|
|
||||||
if background {
|
|
||||||
go s.runBackgroundActions()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runs through different actions once a server's configuration has been persisted
|
// Updates the environment for the server to match any of the changed data. This pushes new settings and
|
||||||
// to the disk. This function does not return anything as any failures should be logged
|
// environment variables to the environment. In addition, the in-situ update method is called on the
|
||||||
// but have no effect on actually updating the server itself.
|
// environment which will allow environments that make use of it (such as Docker) to immediately apply
|
||||||
|
// some settings without having to wait on a server to restart.
|
||||||
//
|
//
|
||||||
// These tasks run in independent threads where relevant to speed up any updates
|
// This functionality allows a server's resources limits to be modified on the fly and have them apply
|
||||||
// that need to happen.
|
// right away allowing for dynamic resource allocation and responses to abusive server processes.
|
||||||
func (s *Server) runBackgroundActions() {
|
func (s *Server) SyncWithEnvironment() {
|
||||||
// Check if the s is now suspended, and if so and the process is not terminated
|
s.Log().Debug("syncing server settings with environment")
|
||||||
// yet, do it immediately.
|
|
||||||
if s.IsSuspended() && s.GetState() != environment.ProcessOfflineState {
|
|
||||||
s.Log().Info("server suspended with running process state, terminating now")
|
|
||||||
|
|
||||||
if err := s.Environment.WaitForStop(10, true); err != nil {
|
// Update the environment settings using the new information from this server.
|
||||||
s.Log().WithField("error", err).Warn("failed to terminate server environment after suspension")
|
s.Environment.Config().SetSettings(environment.Settings{
|
||||||
}
|
Mounts: s.Mounts(),
|
||||||
}
|
Allocations: s.Config().Allocations,
|
||||||
|
Limits: s.Config().Build,
|
||||||
|
})
|
||||||
|
|
||||||
|
// If build limits are changed, environment variables also change. Plus, any modifications to
|
||||||
|
// the startup command also need to be properly propagated to this environment.
|
||||||
|
//
|
||||||
|
// @see https://github.com/pterodactyl/panel/issues/2255
|
||||||
|
s.Environment.Config().SetEnvironmentVariables(s.GetEnvironmentVariables())
|
||||||
|
|
||||||
if !s.IsSuspended() {
|
if !s.IsSuspended() {
|
||||||
// Update the environment in place, allowing memory and CPU usage to be adjusted
|
// Update the environment in place, allowing memory and CPU usage to be adjusted
|
||||||
// on the fly without the user needing to reboot (theoretically).
|
// on the fly without the user needing to reboot (theoretically).
|
||||||
s.Log().Info("performing server limit modification on-the-fly")
|
s.Log().Info("performing server limit modification on-the-fly")
|
||||||
if err := s.Environment.InSituUpdate(); err != nil {
|
if err := s.Environment.InSituUpdate(); err != nil {
|
||||||
|
// This is not a failure, the process is still running fine and will fix itself on the
|
||||||
|
// next boot, or fail out entirely in a more logical position.
|
||||||
s.Log().WithField("error", err).Warn("failed to perform on-the-fly update of the server environment")
|
s.Log().WithField("error", err).Warn("failed to perform on-the-fly update of the server environment")
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Checks if the server is now in a suspended state. If so and a server process is currently running it
|
||||||
|
// will be gracefully stopped (and terminated if it refuses to stop).
|
||||||
|
s.Log().Info("server suspended with running process state, terminating now")
|
||||||
|
|
||||||
|
go func (s *Server) {
|
||||||
|
if err := s.Environment.WaitForStop(60, true); err != nil {
|
||||||
|
s.Log().WithField("error", err).Warn("failed to terminate server environment after suspension")
|
||||||
|
}
|
||||||
|
}(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19
sftp/errors.go
Normal file
19
sftp/errors.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package sftp
|
||||||
|
|
||||||
|
type fxerr uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Extends the default SFTP server to return a quota exceeded error to the client.
|
||||||
|
//
|
||||||
|
// @see https://tools.ietf.org/id/draft-ietf-secsh-filexfer-13.txt
|
||||||
|
ErrSshQuotaExceeded = fxerr(15)
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e fxerr) Error() string {
|
||||||
|
switch e {
|
||||||
|
case ErrSshQuotaExceeded:
|
||||||
|
return "Quota Exceeded"
|
||||||
|
default:
|
||||||
|
return "Failure"
|
||||||
|
}
|
||||||
|
}
|
||||||
380
sftp/handler.go
Normal file
380
sftp/handler.go
Normal file
@@ -0,0 +1,380 @@
|
|||||||
|
package sftp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/apex/log"
|
||||||
|
"github.com/patrickmn/go-cache"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/pkg/sftp"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileSystem struct {
|
||||||
|
UUID string
|
||||||
|
Permissions []string
|
||||||
|
ReadOnly bool
|
||||||
|
User SftpUser
|
||||||
|
Cache *cache.Cache
|
||||||
|
|
||||||
|
PathValidator func(fs FileSystem, p string) (string, error)
|
||||||
|
HasDiskSpace func(fs FileSystem) bool
|
||||||
|
|
||||||
|
logger *log.Entry
|
||||||
|
lock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs FileSystem) buildPath(p string) (string, error) {
|
||||||
|
return fs.PathValidator(fs, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
PermissionFileRead = "file.read"
|
||||||
|
PermissionFileReadContent = "file.read-content"
|
||||||
|
PermissionFileCreate = "file.create"
|
||||||
|
PermissionFileUpdate = "file.update"
|
||||||
|
PermissionFileDelete = "file.delete"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fileread creates a reader for a file on the system and returns the reader back.
|
||||||
|
func (fs FileSystem) Fileread(request *sftp.Request) (io.ReaderAt, error) {
|
||||||
|
// Check first if the user can actually open and view a file. This permission is named
|
||||||
|
// really poorly, but it is checking if they can read. There is an addition permission,
|
||||||
|
// "save-files" which determines if they can write that file.
|
||||||
|
if !fs.can(PermissionFileReadContent) {
|
||||||
|
return nil, sftp.ErrSshFxPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := fs.buildPath(request.Filepath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, sftp.ErrSshFxNoSuchFile
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.lock.Lock()
|
||||||
|
defer fs.lock.Unlock()
|
||||||
|
|
||||||
|
if _, err := os.Stat(p); os.IsNotExist(err) {
|
||||||
|
return nil, sftp.ErrSshFxNoSuchFile
|
||||||
|
} else if err != nil {
|
||||||
|
fs.logger.WithField("error", errors.WithStack(err)).Error("error while processing file stat")
|
||||||
|
|
||||||
|
return nil, sftp.ErrSshFxFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(p)
|
||||||
|
if err != nil {
|
||||||
|
fs.logger.WithField("source", p).WithField("error", errors.WithStack(err)).Error("could not open file for reading")
|
||||||
|
return nil, sftp.ErrSshFxFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filewrite handles the write actions for a file on the system.
|
||||||
|
func (fs FileSystem) Filewrite(request *sftp.Request) (io.WriterAt, error) {
|
||||||
|
if fs.ReadOnly {
|
||||||
|
return nil, sftp.ErrSshFxOpUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := fs.buildPath(request.Filepath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, sftp.ErrSshFxNoSuchFile
|
||||||
|
}
|
||||||
|
|
||||||
|
var l = fs.logger.WithField("source", p)
|
||||||
|
|
||||||
|
// If the user doesn't have enough space left on the server it should respond with an
|
||||||
|
// error since we won't be letting them write this file to the disk.
|
||||||
|
if !fs.HasDiskSpace(fs) {
|
||||||
|
return nil, ErrSshQuotaExceeded
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.lock.Lock()
|
||||||
|
defer fs.lock.Unlock()
|
||||||
|
|
||||||
|
stat, statErr := os.Stat(p)
|
||||||
|
// If the file doesn't exist we need to create it, as well as the directory pathway
|
||||||
|
// leading up to where that file will be created.
|
||||||
|
if os.IsNotExist(statErr) {
|
||||||
|
// This is a different pathway than just editing an existing file. If it doesn't exist already
|
||||||
|
// we need to determine if this user has permission to create files.
|
||||||
|
if !fs.can(PermissionFileCreate) {
|
||||||
|
return nil, sftp.ErrSshFxPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create all of the directories leading up to the location where this file is being created.
|
||||||
|
if err := os.MkdirAll(filepath.Dir(p), 0755); err != nil {
|
||||||
|
l.WithFields(log.Fields{
|
||||||
|
"path": filepath.Dir(p),
|
||||||
|
"error": errors.WithStack(err),
|
||||||
|
}).Error("error making path for file")
|
||||||
|
|
||||||
|
return nil, sftp.ErrSshFxFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Create(p)
|
||||||
|
if err != nil {
|
||||||
|
l.WithField("error", errors.WithStack(err)).Error("failed to create file")
|
||||||
|
|
||||||
|
return nil, sftp.ErrSshFxFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not failing here is intentional. We still made the file, it is just owned incorrectly
|
||||||
|
// and will likely cause some issues.
|
||||||
|
if err := os.Chown(p, fs.User.Uid, fs.User.Gid); err != nil {
|
||||||
|
l.WithField("error", errors.WithStack(err)).Warn("failed to set permissions on file")
|
||||||
|
}
|
||||||
|
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the stat error isn't about the file not existing, there is some other issue
|
||||||
|
// at play and we need to go ahead and bail out of the process.
|
||||||
|
if statErr != nil {
|
||||||
|
l.WithField("error", errors.WithStack(statErr)).Error("encountered error performing file stat")
|
||||||
|
|
||||||
|
return nil, sftp.ErrSshFxFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've made it here it means the file already exists and we don't need to do anything
|
||||||
|
// fancy to handle it. Just pass over the request flags so the system knows what the end
|
||||||
|
// goal with the file is going to be.
|
||||||
|
//
|
||||||
|
// But first, check that the user has permission to save modified files.
|
||||||
|
if !fs.can(PermissionFileUpdate) {
|
||||||
|
return nil, sftp.ErrSshFxPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not sure this would ever happen, but lets not find out.
|
||||||
|
if stat.IsDir() {
|
||||||
|
return nil, sftp.ErrSshFxOpUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Create(p)
|
||||||
|
if err != nil {
|
||||||
|
// Prevent errors if the file is deleted between the stat and this call.
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, sftp.ErrSSHFxNoSuchFile
|
||||||
|
}
|
||||||
|
|
||||||
|
l.WithField("flags", request.Flags).WithField("error", errors.WithStack(err)).Error("failed to open existing file on system")
|
||||||
|
return nil, sftp.ErrSshFxFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not failing here is intentional. We still made the file, it is just owned incorrectly
|
||||||
|
// and will likely cause some issues.
|
||||||
|
if err := os.Chown(p, fs.User.Uid, fs.User.Gid); err != nil {
|
||||||
|
l.WithField("error", errors.WithStack(err)).Warn("error chowning file")
|
||||||
|
}
|
||||||
|
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filecmd hander for basic SFTP system calls related to files, but not anything to do with reading
|
||||||
|
// or writing to those files.
|
||||||
|
func (fs FileSystem) Filecmd(request *sftp.Request) error {
|
||||||
|
if fs.ReadOnly {
|
||||||
|
return sftp.ErrSshFxOpUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := fs.buildPath(request.Filepath)
|
||||||
|
if err != nil {
|
||||||
|
return sftp.ErrSshFxNoSuchFile
|
||||||
|
}
|
||||||
|
|
||||||
|
var l = fs.logger.WithField("source", p)
|
||||||
|
|
||||||
|
var target string
|
||||||
|
// If a target is provided in this request validate that it is going to the correct
|
||||||
|
// location for the server. If it is not, return an operation unsupported error. This
|
||||||
|
// is maybe not the best error response, but its not wrong either.
|
||||||
|
if request.Target != "" {
|
||||||
|
target, err = fs.buildPath(request.Target)
|
||||||
|
if err != nil {
|
||||||
|
return sftp.ErrSshFxOpUnsupported
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch request.Method {
|
||||||
|
case "Setstat":
|
||||||
|
if !fs.can(PermissionFileUpdate) {
|
||||||
|
return sftp.ErrSshFxPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
var mode os.FileMode = 0644
|
||||||
|
// If the client passed a valid file permission use that, otherwise use the
|
||||||
|
// default of 0644 set above.
|
||||||
|
if request.Attributes().FileMode().Perm() != 0000 {
|
||||||
|
mode = request.Attributes().FileMode().Perm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force directories to be 0755
|
||||||
|
if request.Attributes().FileMode().IsDir() {
|
||||||
|
mode = 0755
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Chmod(p, mode); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return sftp.ErrSSHFxNoSuchFile
|
||||||
|
}
|
||||||
|
|
||||||
|
l.WithField("error", errors.WithStack(err)).Error("failed to perform setstat on item")
|
||||||
|
return sftp.ErrSSHFxFailure
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case "Rename":
|
||||||
|
if !fs.can(PermissionFileUpdate) {
|
||||||
|
return sftp.ErrSSHFxPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Rename(p, target); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return sftp.ErrSSHFxNoSuchFile
|
||||||
|
}
|
||||||
|
|
||||||
|
l.WithField("target", target).WithField("error", errors.WithStack(err)).Error("failed to rename file")
|
||||||
|
|
||||||
|
return sftp.ErrSshFxFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
case "Rmdir":
|
||||||
|
if !fs.can(PermissionFileDelete) {
|
||||||
|
return sftp.ErrSshFxPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.RemoveAll(p); err != nil {
|
||||||
|
l.WithField("error", errors.WithStack(err)).Error("failed to remove directory")
|
||||||
|
|
||||||
|
return sftp.ErrSshFxFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
return sftp.ErrSshFxOk
|
||||||
|
case "Mkdir":
|
||||||
|
if !fs.can(PermissionFileCreate) {
|
||||||
|
return sftp.ErrSshFxPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(p, 0755); err != nil {
|
||||||
|
l.WithField("error", errors.WithStack(err)).Error("failed to create directory")
|
||||||
|
|
||||||
|
return sftp.ErrSshFxFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
case "Symlink":
|
||||||
|
if !fs.can(PermissionFileCreate) {
|
||||||
|
return sftp.ErrSshFxPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Symlink(p, target); err != nil {
|
||||||
|
l.WithField("target", target).WithField("error", errors.WithStack(err)).Error("failed to create symlink")
|
||||||
|
|
||||||
|
return sftp.ErrSshFxFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
case "Remove":
|
||||||
|
if !fs.can(PermissionFileDelete) {
|
||||||
|
return sftp.ErrSshFxPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Remove(p); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return sftp.ErrSSHFxNoSuchFile
|
||||||
|
}
|
||||||
|
|
||||||
|
l.WithField("error", errors.WithStack(err)).Error("failed to remove a file")
|
||||||
|
|
||||||
|
return sftp.ErrSshFxFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
return sftp.ErrSshFxOk
|
||||||
|
default:
|
||||||
|
return sftp.ErrSshFxOpUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileLocation = p
|
||||||
|
if target != "" {
|
||||||
|
fileLocation = target
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not failing here is intentional. We still made the file, it is just owned incorrectly
|
||||||
|
// and will likely cause some issues. There is no logical check for if the file was removed
|
||||||
|
// because both of those cases (Rmdir, Remove) have an explicit return rather than break.
|
||||||
|
if err := os.Chown(fileLocation, fs.User.Uid, fs.User.Gid); err != nil {
|
||||||
|
l.WithField("error", errors.WithStack(err)).Warn("error chowning file")
|
||||||
|
}
|
||||||
|
|
||||||
|
return sftp.ErrSshFxOk
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filelist is the handler for SFTP filesystem list calls. This will handle calls to list the contents of
|
||||||
|
// a directory as well as perform file/folder stat calls.
|
||||||
|
func (fs FileSystem) Filelist(request *sftp.Request) (sftp.ListerAt, error) {
|
||||||
|
p, err := fs.buildPath(request.Filepath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, sftp.ErrSshFxNoSuchFile
|
||||||
|
}
|
||||||
|
|
||||||
|
switch request.Method {
|
||||||
|
case "List":
|
||||||
|
if !fs.can(PermissionFileRead) {
|
||||||
|
return nil, sftp.ErrSshFxPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
files, err := ioutil.ReadDir(p)
|
||||||
|
if err != nil {
|
||||||
|
fs.logger.WithField("error", errors.WithStack(err)).Error("error while listing directory")
|
||||||
|
|
||||||
|
return nil, sftp.ErrSshFxFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListerAt(files), nil
|
||||||
|
case "Stat":
|
||||||
|
if !fs.can(PermissionFileRead) {
|
||||||
|
return nil, sftp.ErrSshFxPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := os.Stat(p)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, sftp.ErrSshFxNoSuchFile
|
||||||
|
} else if err != nil {
|
||||||
|
fs.logger.WithField("source", p).WithField("error", errors.WithStack(err)).Error("error performing stat on file")
|
||||||
|
|
||||||
|
return nil, sftp.ErrSshFxFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListerAt([]os.FileInfo{s}), nil
|
||||||
|
default:
|
||||||
|
// Before adding readlink support we need to evaluate any potential security risks
|
||||||
|
// as a result of navigating around to a location that is outside the home directory
|
||||||
|
// for the logged in user. I don't forsee it being much of a problem, but I do want to
|
||||||
|
// check it out before slapping some code here. Until then, we'll just return an
|
||||||
|
// unsupported response code.
|
||||||
|
return nil, sftp.ErrSshFxOpUnsupported
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determines if a user has permission to perform a specific action on the SFTP server. These
|
||||||
|
// permissions are defined and returned by the Panel API.
|
||||||
|
func (fs FileSystem) can(permission string) bool {
|
||||||
|
// Server owners and super admins have their permissions returned as '[*]' via the Panel
|
||||||
|
// API, so for the sake of speed do an initial check for that before iterating over the
|
||||||
|
// entire array of permissions.
|
||||||
|
if len(fs.Permissions) == 1 && fs.Permissions[0] == "*" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not the owner or an admin, loop over the permissions that were returned to determine
|
||||||
|
// if they have the passed permission.
|
||||||
|
for _, p := range fs.Permissions {
|
||||||
|
if p == permission {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
22
sftp/lister.go
Normal file
22
sftp/lister.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package sftp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListerAt []os.FileInfo
|
||||||
|
|
||||||
|
// Returns the number of entries copied and an io.EOF error if we made it to the end of the file list.
|
||||||
|
// Take a look at the pkg/sftp godoc for more information about how this function should work.
|
||||||
|
func (l ListerAt) ListAt(f []os.FileInfo, offset int64) (int, error) {
|
||||||
|
if offset >= int64(len(l)) {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if n := copy(f, l[offset:]); n < len(f) {
|
||||||
|
return n, io.EOF
|
||||||
|
} else {
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
308
sftp/server.go
308
sftp/server.go
@@ -1,118 +1,238 @@
|
|||||||
package sftp
|
package sftp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/pkg/errors"
|
"github.com/patrickmn/go-cache"
|
||||||
"github.com/pterodactyl/sftp-server"
|
"github.com/pkg/sftp"
|
||||||
"github.com/pterodactyl/wings/api"
|
"github.com/pterodactyl/wings/api"
|
||||||
"github.com/pterodactyl/wings/config"
|
"golang.org/x/crypto/ssh"
|
||||||
"github.com/pterodactyl/wings/server"
|
"io"
|
||||||
"go.uber.org/zap"
|
"io/ioutil"
|
||||||
"regexp"
|
"net"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Initialize(config *config.Configuration) error {
|
type Settings struct {
|
||||||
c := &sftp_server.Server{
|
BasePath string
|
||||||
User: sftp_server.SftpUser{
|
ReadOnly bool
|
||||||
Uid: config.System.User.Uid,
|
BindPort int
|
||||||
Gid: config.System.User.Gid,
|
BindAddress string
|
||||||
},
|
}
|
||||||
Settings: sftp_server.Settings{
|
|
||||||
BasePath: config.System.Data,
|
|
||||||
ReadOnly: config.System.Sftp.ReadOnly,
|
|
||||||
BindAddress: config.System.Sftp.Address,
|
|
||||||
BindPort: config.System.Sftp.Port,
|
|
||||||
},
|
|
||||||
CredentialValidator: validateCredentials,
|
|
||||||
PathValidator: validatePath,
|
|
||||||
DiskSpaceValidator: validateDiskSpace,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := sftp_server.New(c); err != nil {
|
type SftpUser struct {
|
||||||
return err
|
Uid int
|
||||||
}
|
Gid int
|
||||||
|
}
|
||||||
|
|
||||||
c.ConfigureLogger(func() *zap.SugaredLogger {
|
type Server struct {
|
||||||
return zap.S().Named("sftp")
|
cache *cache.Cache
|
||||||
})
|
|
||||||
|
|
||||||
// Initialize the SFTP server in a background thread since this is
|
Settings Settings
|
||||||
// a long running operation.
|
User SftpUser
|
||||||
go func(instance *sftp_server.Server) {
|
|
||||||
if err := c.Initialize(); err != nil {
|
PathValidator func(fs FileSystem, p string) (string, error)
|
||||||
log.WithField("subsystem", "sftp").WithField("error", errors.WithStack(err)).Error("failed to initialize SFTP subsystem")
|
DiskSpaceValidator func(fs FileSystem) bool
|
||||||
}
|
|
||||||
}(c)
|
// Validator function that is called when a user connects to the server. This should
|
||||||
|
// check against whatever system is desired to confirm if the given username and password
|
||||||
|
// combination is valid. If so, should return an authentication response.
|
||||||
|
CredentialValidator func(r api.SftpAuthRequest) (*api.SftpAuthResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new server configuration instance.
|
||||||
|
func New(c *Server) error {
|
||||||
|
c.cache = cache.New(5*time.Minute, 10*time.Minute)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validatePath(fs sftp_server.FileSystem, p string) (string, error) {
|
// Initialize the SFTP server and add a persistent listener to handle inbound SFTP connections.
|
||||||
s := server.GetServers().Find(func(server *server.Server) bool {
|
func (c *Server) Initialize() error {
|
||||||
return server.Id() == fs.UUID
|
serverConfig := &ssh.ServerConfig{
|
||||||
})
|
NoClientAuth: false,
|
||||||
|
MaxAuthTries: 6,
|
||||||
|
PasswordCallback: func(conn ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
|
||||||
|
resp, err := c.CredentialValidator(api.SftpAuthRequest{
|
||||||
|
User: conn.User(),
|
||||||
|
Pass: string(pass),
|
||||||
|
IP: conn.RemoteAddr().String(),
|
||||||
|
SessionID: conn.SessionID(),
|
||||||
|
ClientVersion: conn.ClientVersion(),
|
||||||
|
})
|
||||||
|
|
||||||
if s == nil {
|
if err != nil {
|
||||||
return "", errors.New("no server found with that UUID")
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sshPerm := &ssh.Permissions{
|
||||||
|
Extensions: map[string]string{
|
||||||
|
"uuid": resp.Server,
|
||||||
|
"user": conn.User(),
|
||||||
|
"permissions": strings.Join(resp.Permissions, ","),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return sshPerm, nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.Filesystem.SafePath(p)
|
if _, err := os.Stat(path.Join(c.Settings.BasePath, ".sftp/id_rsa")); os.IsNotExist(err) {
|
||||||
}
|
if err := c.generatePrivateKey(); err != nil {
|
||||||
|
return err
|
||||||
func validateDiskSpace(fs sftp_server.FileSystem) bool {
|
}
|
||||||
s := server.GetServers().Find(func(server *server.Server) bool {
|
} else if err != nil {
|
||||||
return server.Id() == fs.UUID
|
return err
|
||||||
})
|
|
||||||
|
|
||||||
if s == nil {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.Filesystem.HasSpaceAvailable()
|
privateBytes, err := ioutil.ReadFile(path.Join(c.Settings.BasePath, ".sftp/id_rsa"))
|
||||||
}
|
|
||||||
|
|
||||||
var validUsernameRegexp = regexp.MustCompile(`^(?i)(.+)\.([a-z0-9]{8})$`)
|
|
||||||
|
|
||||||
// Validates a set of credentials for a SFTP login aganist Pterodactyl Panel and returns
|
|
||||||
// the server's UUID if the credentials were valid.
|
|
||||||
func validateCredentials(c sftp_server.AuthenticationRequest) (*sftp_server.AuthenticationResponse, error) {
|
|
||||||
log.WithFields(log.Fields{"subsystem": "sftp", "username": c.User}).Debug("validating credentials for SFTP connection")
|
|
||||||
|
|
||||||
f := log.Fields{
|
|
||||||
"subsystem": "sftp",
|
|
||||||
"username": c.User,
|
|
||||||
"ip": c.IP,
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the username doesn't meet the expected format that the Panel would even recognize just go ahead
|
|
||||||
// and bail out of the process here to avoid accidentially brute forcing the panel if a bot decides
|
|
||||||
// to connect to spam username attempts.
|
|
||||||
if !validUsernameRegexp.MatchString(c.User) {
|
|
||||||
log.WithFields(f).Warn("failed to validate user credentials (invalid format)")
|
|
||||||
|
|
||||||
return nil, new(sftp_server.InvalidCredentialsError)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := api.NewRequester().ValidateSftpCredentials(c)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if sftp_server.IsInvalidCredentialsError(err) {
|
return err
|
||||||
log.WithFields(f).Warn("failed to validate user credentials (invalid username or password)")
|
}
|
||||||
} else {
|
|
||||||
log.WithFields(f).Error("encountered an error while trying to validate user credentials")
|
private, err := ssh.ParsePrivateKey(privateBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add our private key to the server configuration.
|
||||||
|
serverConfig.AddHostKey(private)
|
||||||
|
|
||||||
|
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", c.Settings.BindAddress, c.Settings.BindPort))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.WithField("host", c.Settings.BindAddress).WithField("port", c.Settings.BindPort).Info("sftp subsystem listening for connections")
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn, _ := listener.Accept()
|
||||||
|
if conn != nil {
|
||||||
|
go c.AcceptInboundConnection(conn, serverConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles an inbound connection to the instance and determines if we should serve the request
|
||||||
|
// or not.
|
||||||
|
func (c Server) AcceptInboundConnection(conn net.Conn, config *ssh.ServerConfig) {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
// Before beginning a handshake must be performed on the incoming net.Conn
|
||||||
|
sconn, chans, reqs, err := ssh.NewServerConn(conn, config)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer sconn.Close()
|
||||||
|
|
||||||
|
go ssh.DiscardRequests(reqs)
|
||||||
|
|
||||||
|
for newChannel := range chans {
|
||||||
|
// If its not a session channel we just move on because its not something we
|
||||||
|
// know how to handle at this point.
|
||||||
|
if newChannel.ChannelType() != "session" {
|
||||||
|
newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, err
|
channel, requests, err := newChannel.Accept()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channels have a type that is dependent on the protocol. For SFTP this is "subsystem"
|
||||||
|
// with a payload that (should) be "sftp". Discard anything else we receive ("pty", "shell", etc)
|
||||||
|
go func(in <-chan *ssh.Request) {
|
||||||
|
for req := range in {
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
switch req.Type {
|
||||||
|
case "subsystem":
|
||||||
|
if string(req.Payload[4:]) == "sftp" {
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Reply(ok, nil)
|
||||||
|
}
|
||||||
|
}(requests)
|
||||||
|
|
||||||
|
// Configure the user's home folder for the rest of the request cycle.
|
||||||
|
if sconn.Permissions.Extensions["uuid"] == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new handler for the currently logged in user's server.
|
||||||
|
fs := c.createHandler(sconn)
|
||||||
|
|
||||||
|
// Create the server instance for the channel using the filesystem we created above.
|
||||||
|
server := sftp.NewRequestServer(channel, fs)
|
||||||
|
|
||||||
|
if err := server.Serve(); err == io.EOF {
|
||||||
|
server.Close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
s := server.GetServers().Find(func(server *server.Server) bool {
|
|
||||||
return server.Id() == resp.Server
|
// Creates a new SFTP handler for a given server. The directory argument should
|
||||||
})
|
// be the base directory for a server. All actions done on the server will be
|
||||||
|
// relative to that directory, and the user will not be able to escape out of it.
|
||||||
if s == nil {
|
func (c Server) createHandler(sc *ssh.ServerConn) sftp.Handlers {
|
||||||
return resp, errors.New("no matching server with UUID found")
|
p := FileSystem{
|
||||||
}
|
UUID: sc.Permissions.Extensions["uuid"],
|
||||||
|
Permissions: strings.Split(sc.Permissions.Extensions["permissions"], ","),
|
||||||
s.Log().WithFields(f).Debug("credentials successfully validated and matched user to server instance")
|
ReadOnly: c.Settings.ReadOnly,
|
||||||
|
Cache: c.cache,
|
||||||
return resp, err
|
User: c.User,
|
||||||
|
HasDiskSpace: c.DiskSpaceValidator,
|
||||||
|
PathValidator: c.PathValidator,
|
||||||
|
logger: log.WithFields(log.Fields{
|
||||||
|
"subsystem": "sftp",
|
||||||
|
"username": sc.User(),
|
||||||
|
"ip": sc.RemoteAddr(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
return sftp.Handlers{
|
||||||
|
FileGet: p,
|
||||||
|
FilePut: p,
|
||||||
|
FileCmd: p,
|
||||||
|
FileList: p,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a private key that will be used by the SFTP server.
|
||||||
|
func (c Server) generatePrivateKey() error {
|
||||||
|
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(path.Join(c.Settings.BasePath, ".sftp"), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
o, err := os.OpenFile(path.Join(c.Settings.BasePath, ".sftp/id_rsa"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer o.Close()
|
||||||
|
|
||||||
|
pkey := &pem.Block{
|
||||||
|
Type: "RSA PRIVATE KEY",
|
||||||
|
Bytes: x509.MarshalPKCS1PrivateKey(key),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := pem.Encode(o, pkey); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
97
sftp/sftp.go
Normal file
97
sftp/sftp.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package sftp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/apex/log"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/pterodactyl/wings/api"
|
||||||
|
"github.com/pterodactyl/wings/config"
|
||||||
|
"github.com/pterodactyl/wings/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
var noMatchingServerError = errors.New("no matching server with that UUID was found")
|
||||||
|
|
||||||
|
func Initialize(config config.SystemConfiguration) error {
|
||||||
|
s := &Server{
|
||||||
|
User: SftpUser{
|
||||||
|
Uid: config.User.Uid,
|
||||||
|
Gid: config.User.Gid,
|
||||||
|
},
|
||||||
|
Settings: Settings{
|
||||||
|
BasePath: config.Data,
|
||||||
|
ReadOnly: config.Sftp.ReadOnly,
|
||||||
|
BindAddress: config.Sftp.Address,
|
||||||
|
BindPort: config.Sftp.Port,
|
||||||
|
},
|
||||||
|
CredentialValidator: validateCredentials,
|
||||||
|
PathValidator: validatePath,
|
||||||
|
DiskSpaceValidator: validateDiskSpace,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := New(s); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the SFTP server in a background thread since this is
|
||||||
|
// a long running operation.
|
||||||
|
go func(s *Server) {
|
||||||
|
if err := s.Initialize(); err != nil {
|
||||||
|
log.WithField("subsystem", "sftp").WithField("error", errors.WithStack(err)).Error("failed to initialize SFTP subsystem")
|
||||||
|
}
|
||||||
|
}(s)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validatePath(fs FileSystem, p string) (string, error) {
|
||||||
|
s := server.GetServers().Find(func(server *server.Server) bool {
|
||||||
|
return server.Id() == fs.UUID
|
||||||
|
})
|
||||||
|
|
||||||
|
if s == nil {
|
||||||
|
return "", noMatchingServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Filesystem.SafePath(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateDiskSpace(fs FileSystem) bool {
|
||||||
|
s := server.GetServers().Find(func(server *server.Server) bool {
|
||||||
|
return server.Id() == fs.UUID
|
||||||
|
})
|
||||||
|
|
||||||
|
if s == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Filesystem.HasSpaceAvailable(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validates a set of credentials for a SFTP login aganist Pterodactyl Panel and returns
|
||||||
|
// the server's UUID if the credentials were valid.
|
||||||
|
func validateCredentials(c api.SftpAuthRequest) (*api.SftpAuthResponse, error) {
|
||||||
|
f := log.Fields{"subsystem": "sftp", "username": c.User, "ip": c.IP}
|
||||||
|
|
||||||
|
log.WithFields(f).Debug("validating credentials for SFTP connection")
|
||||||
|
resp, err := api.NewRequester().ValidateSftpCredentials(c)
|
||||||
|
if err != nil {
|
||||||
|
if api.IsInvalidCredentialsError(err) {
|
||||||
|
log.WithFields(f).Warn("failed to validate user credentials (invalid username or password)")
|
||||||
|
} else {
|
||||||
|
log.WithFields(f).Error("encountered an error while trying to validate user credentials")
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s := server.GetServers().Find(func(server *server.Server) bool {
|
||||||
|
return server.Id() == resp.Server
|
||||||
|
})
|
||||||
|
|
||||||
|
if s == nil {
|
||||||
|
return resp, noMatchingServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Log().WithFields(f).Debug("credentials successfully validated and matched user to server instance")
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user