diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml new file mode 100644 index 0000000..8b72274 --- /dev/null +++ b/.github/workflows/build-test.yml @@ -0,0 +1,33 @@ +name: "Build & Test" + +on: + push: + branches-ignore: + - 'master' + - 'release/**' + pull_request: + +jobs: + build: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2 + with: + go-version: '^1.14.2' + + - name: Build + run: GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -ldflags "-X github.com/pterodactyl/wings/system.Version=dev-${GIT_COMMIT:0:7}" -o build/wings_linux_amd64 -v wings.go + + - name: Test + run: go test ./... + + - name: Compress binary and make it executable + if: ${{ github.ref == 'refs/heads/develop' || github.event_name == 'pull_request' }} + run: upx build/wings_linux_amd64 && chmod +x build/wings_linux_amd64 + + - uses: actions/upload-artifact@v2 + if: ${{ github.ref == 'refs/heads/develop' || github.event_name == 'pull_request' }} + with: + name: wings_linux_amd64 + path: build/wings_linux_amd64 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..20fe3b7 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,88 @@ +name: "Release" + +on: + push: + tags: + - 'v*' + +jobs: + release: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2 + with: + go-version: '^1.14.2' + + - name: Build + env: + REF: ${{ github.ref }} + run: GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -ldflags "-X github.com/pterodactyl/wings/system.Version=dev-${GIT_COMMIT:0:7}" -o build/wings_linux_amd64 -v wings.go + + - name: Test + run: go test ./... + + - name: Compress binary and make it executable + run: upx build/wings_linux_amd64 && chmod +x build/wings_linux_amd64 + + - name: Extract changelog + env: + REF: ${{ github.ref }} + run: | + sed -n "/^## ${REF:10}/,/^## /{/^## /b;p}" CHANGELOG.md > ./RELEASE_CHANGELOG + echo ::set-output name=version_name::`sed -nr "s/^## (${REF:10} .*)$/\1/p" CHANGELOG.md` + + + - name: Create checksum and add to changelog + run: | + SUM=`cd build && sha256sum wings_linux_amd64` + echo -e "\n#### SHA256 Checksum\n\n\`\`\`\n$SUM\n\`\`\`\n" >> ./RELEASE_CHANGELOG + echo $SUM > checksum.txt + + - name: Create release branch + env: + REF: ${{ github.ref }} + run: | + BRANCH=release/${REF:10} + git config --local user.email "ci@pterodactyl.io" + git config --local user.name "Pterodactyl CI" + git checkout -b $BRANCH + git push -u origin $BRANCH + sed -i "s/ Version = \".*\"/ Version = \"${REF:11}\"/" config/app.php + git add config/app.php + git commit -m "bump version for release" + git push + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: ${{ steps.extract_changelog.outputs.version_name }} + body_path: ./RELEASE_CHANGELOG + draft: true + prerelease: ${{ contains(github.ref, 'beta') || contains(github.ref, 'alpha') }} + + - name: Upload binary + id: upload-release-binary + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: build/wings_linux_amd64 + asset_name: wings_linux_amd64 + asset_content_type: application/octet-stream + + - name: Upload checksum + id: upload-release-checksum + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./checksum.txt + asset_name: checksum.txt + asset_content_type: text/plain diff --git a/cmd/diagnostics.go b/cmd/diagnostics.go new file mode 100644 index 0000000..5509100 --- /dev/null +++ b/cmd/diagnostics.go @@ -0,0 +1,226 @@ +package cmd + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "os/exec" + "path" + "strings" + + "github.com/AlecAivazis/survey/v2" + "github.com/AlecAivazis/survey/v2/terminal" + "github.com/docker/cli/components/engine/pkg/parsers/operatingsystem" + "github.com/docker/docker/api/types" + "github.com/docker/docker/client" + "github.com/docker/docker/pkg/parsers/kernel" + "github.com/pterodactyl/wings/config" + "github.com/pterodactyl/wings/system" + "github.com/spf13/cobra" +) + +const DefaultHastebinUrl = "https://hastebin.com" + +var ( + diagnosticsArgs struct { + IncludeEndpoints bool + IncludeLogs bool + ReviewBeforeUpload bool + HastebinURL string + } +) + +var diagnosticsCmd = &cobra.Command{ + Use: "diagnostics", + Short: "Collect diagnostics information.", + Run: diagnosticsCmdRun, +} + +func init() { + diagnosticsCmd.PersistentFlags().StringVar(&diagnosticsArgs.HastebinURL, "hastebin-url", DefaultHastebinUrl, "The url of the hastebin instance to use.") +} + +// diagnosticsCmdRun collects diagnostics about wings, it's configuration and the node. +// We collect: +// - wings and docker versions +// - relevant parts of daemon configuration +// - the docker debug output +// - running docker containers +// - logs +func diagnosticsCmdRun(cmd *cobra.Command, args []string) { + questions := []*survey.Question{ + { + Name: "IncludeEndpoints", + Prompt: &survey.Confirm{Message: "Do you want to include endpoints (i.e. the FQDN/IP of your panel)?", Default: false}, + }, + { + Name: "IncludeLogs", + Prompt: &survey.Confirm{Message: "Do you want to include the latest logs?", Default: true}, + }, + { + Name: "ReviewBeforeUpload", + Prompt: &survey.Confirm{ + Message: "Do you want to review the collected data before uploading to hastebin.com?", + Help: "The data, especially the logs, might contain sensitive information, so you should review it. You will be asked again if you want to uplaod.", + Default: true, + }, + }, + } + if err := survey.Ask(questions, &diagnosticsArgs); err != nil { + if err == terminal.InterruptErr { + return + } + panic(err) + } + + dockerVersion, dockerInfo, dockerErr := getDockerInfo() + _ = dockerInfo + + output := &strings.Builder{} + fmt.Fprintln(output, "Pterodactly Wings - Diagnostics Report") + printHeader(output, "Versions") + fmt.Fprintln(output, "wings:", system.Version) + if dockerErr == nil { + fmt.Fprintln(output, "Docker", dockerVersion.Version) + } + if v, err := kernel.GetKernelVersion(); err == nil { + fmt.Fprintln(output, "Kernel:", v) + } + if os, err := operatingsystem.GetOperatingSystem(); err == nil { + fmt.Fprintln(output, "OS:", os) + } + + printHeader(output, "Wings Configuration") + if cfg, err := config.ReadConfiguration(config.DefaultLocation); cfg != nil { + fmt.Fprintln(output, "Panel Location:", redact(cfg.PanelLocation)) + fmt.Fprintln(output, "Api Host:", redact(cfg.Api.Host)) + fmt.Fprintln(output, "Api Port:", cfg.Api.Port) + fmt.Fprintln(output, "Api Ssl Enabled:", cfg.Api.Ssl.Enabled) + fmt.Fprintln(output, "Api Ssl Certificate:", redact(cfg.Api.Ssl.CertificateFile)) + fmt.Fprintln(output, "Api Ssl Key:", redact(cfg.Api.Ssl.KeyFile)) + fmt.Fprintln(output, "Sftp Address:", redact(cfg.System.Sftp.Address)) + fmt.Fprintln(output, "Sftp Port:", cfg.System.Sftp.Port) + fmt.Fprintln(output, "Sftp Read Only:", cfg.System.Sftp.ReadOnly) + fmt.Fprintln(output, "Sftp Diskchecking Disabled:", cfg.System.Sftp.DisableDiskChecking) + fmt.Fprintln(output, "System Root Directory:", cfg.System.RootDirectory) + fmt.Fprintln(output, "System Logs Directory:", cfg.System.LogDirectory) + fmt.Fprintln(output, "System Data Directory:", cfg.System.Data) + fmt.Fprintln(output, "System Archive Directory:", cfg.System.ArchiveDirectory) + fmt.Fprintln(output, "System Backup Directory:", cfg.System.BackupDirectory) + fmt.Fprintln(output, "System Username:", cfg.System.Username) + fmt.Fprintln(output, "Debug Enabled:", cfg.Debug) + } else { + fmt.Println("Failed to load configuration.", err) + } + + printHeader(output, "Docker: Info") + fmt.Fprintln(output, "Server Version:", dockerInfo.ServerVersion) + fmt.Fprintln(output, "Storage Driver:", dockerInfo.Driver) + if dockerInfo.DriverStatus != nil { + for _, pair := range dockerInfo.DriverStatus { + fmt.Fprintf(output, " %s: %s\n", pair[0], pair[1]) + } + } + if dockerInfo.SystemStatus != nil { + for _, pair := range dockerInfo.SystemStatus { + fmt.Fprintf(output, " %s: %s\n", pair[0], pair[1]) + } + } + fmt.Fprintln(output, "LoggingDriver:", dockerInfo.LoggingDriver) + fmt.Fprintln(output, "CgroupDriver:", dockerInfo.CgroupDriver) + if len(dockerInfo.Warnings) > 0 { + for _, w := range dockerInfo.Warnings { + fmt.Fprintln(output, w) + } + } + + printHeader(output, "Docker: Running Containers") + c := exec.Command("docker", "ps") + if co, err := c.Output(); err == nil { + output.Write(co) + } else { + fmt.Fprint(output, "Couldn't list containers: ", err) + } + + printHeader(output, "Latest Wings Logs") + if diagnosticsArgs.IncludeLogs { + fmt.Fprintln(output, "No logs found. Probably because nobody implemented logging to files yet :(") + } else { + fmt.Fprintln(output, "Logs redacted.") + } + + fmt.Println("\n--------------- generated report ---------------") + fmt.Println(output.String()) + fmt.Print("--------------- end of report ---------------\n\n") + + upload := !diagnosticsArgs.ReviewBeforeUpload + if !upload { + survey.AskOne(&survey.Confirm{Message: "Upload to " + diagnosticsArgs.HastebinURL + "?", Default: false}, &upload) + } + if upload { + url, err := uploadToHastebin(diagnosticsArgs.HastebinURL, output.String()) + if err == nil { + fmt.Println("Your report is available here: ", url) + } + } +} + +func getDockerInfo() (types.Version, types.Info, error) { + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + return types.Version{}, types.Info{}, err + } + dockerVersion, err := cli.ServerVersion(context.Background()) + if err != nil { + return types.Version{}, types.Info{}, err + } + dockerInfo, err := cli.Info(context.Background()) + if err != nil { + return types.Version{}, types.Info{}, err + } + return dockerVersion, dockerInfo, nil +} + +func uploadToHastebin(hbUrl, content string) (string, error) { + r := strings.NewReader(content) + u, err := url.Parse(hbUrl) + if err != nil { + return "", err + } + u.Path = path.Join(u.Path, "documents") + res, err := http.Post(u.String(), "plain/text", r) + if err != nil || res.StatusCode != 200 { + fmt.Println("Failed to upload report to ", u.String(), err) + return "", err + } + pres := make(map[string]interface{}) + body, err := ioutil.ReadAll(res.Body) + if err != nil { + fmt.Println("Failed to parse response.", err) + return "", err + } + json.Unmarshal(body, &pres) + if key, ok := pres["key"].(string); ok { + u, _ := url.Parse(hbUrl) + u.Path = path.Join(u.Path, key) + return u.String(), nil + } + return "", errors.New("Couldn't find key in response") +} + +func redact(s string) string { + if !diagnosticsArgs.IncludeEndpoints { + return "{redacted}" + } + return s +} + +func printHeader(w io.Writer, title string) { + fmt.Fprintln(w, "\n|\n|", title) + fmt.Fprintln(w, "| ------------------------------") +} diff --git a/cmd/root.go b/cmd/root.go index 2f9bdf8..60b5d31 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -3,15 +3,16 @@ package cmd import ( "crypto/tls" "fmt" - "github.com/apex/log" - "github.com/mitchellh/colorstring" - "github.com/pterodactyl/wings/loggers/cli" - "golang.org/x/crypto/acme/autocert" "net/http" "os" "path" "strings" + "github.com/apex/log" + "github.com/mitchellh/colorstring" + "github.com/pterodactyl/wings/loggers/cli" + "golang.org/x/crypto/acme/autocert" + "github.com/pkg/errors" "github.com/pkg/profile" "github.com/pterodactyl/wings/config" @@ -30,6 +31,7 @@ var debug = false var shouldRunProfiler = false var useAutomaticTls = false var tlsHostname = "" +var showVersion = false var root = &cobra.Command{ Use: "wings", @@ -41,10 +43,11 @@ var root = &cobra.Command{ os.Exit(1) } }, - Run: rootCmdRun, + Run: rootCmdRun, } func init() { + root.PersistentFlags().BoolVar(&showVersion, "version", false, "show the version and exit") root.PersistentFlags().StringVar(&configPath, "config", config.DefaultLocation, "set the location for the configuration file") root.PersistentFlags().BoolVar(&debug, "debug", false, "pass in order to run wings in debug mode") root.PersistentFlags().BoolVar(&shouldRunProfiler, "profile", false, "pass in order to profile wings") @@ -52,6 +55,7 @@ func init() { root.PersistentFlags().StringVar(&tlsHostname, "tls-hostname", "", "required with --auto-tls, the FQDN for the generated SSL certificate") root.AddCommand(configureCmd) + root.AddCommand(diagnosticsCmd) } // Get the configuration path based on the arguments provided. @@ -76,6 +80,11 @@ func readConfiguration() (*config.Configuration, error) { } func rootCmdRun(*cobra.Command, []string) { + if showVersion { + fmt.Println(system.Version) + os.Exit(0) + } + if shouldRunProfiler { defer profile.Start().Stop() } @@ -229,10 +238,10 @@ func rootCmdRun(*cobra.Command, []string) { } log.WithFields(log.Fields{ - "use_ssl": c.Api.Ssl.Enabled, + "use_ssl": c.Api.Ssl.Enabled, "use_auto_tls": useAutomaticTls && len(tlsHostname) > 0, "host_address": c.Api.Host, - "host_port": c.Api.Port, + "host_port": c.Api.Port, }).Info("configuring internal webserver") r := router.Configure() @@ -240,9 +249,9 @@ func rootCmdRun(*cobra.Command, []string) { if useAutomaticTls && len(tlsHostname) > 0 { m := autocert.Manager{ - Prompt: autocert.AcceptTOS, - Cache: autocert.DirCache(path.Join(c.System.RootDirectory, "/.tls-cache")), - HostPolicy: autocert.HostWhitelist(tlsHostname), + Prompt: autocert.AcceptTOS, + Cache: autocert.DirCache(path.Join(c.System.RootDirectory, "/.tls-cache")), + HostPolicy: autocert.HostWhitelist(tlsHostname), } log.WithField("hostname", tlsHostname). diff --git a/config/config_docker.go b/config/config_docker.go index 2828356..40131e4 100644 --- a/config/config_docker.go +++ b/config/config_docker.go @@ -39,6 +39,9 @@ type DockerConfiguration struct { // for containers run through the daemon. Network DockerNetworkConfiguration `json:"network" yaml:"network"` + // Domainname is the Docker domainname for all containers. + Domainname string `default:"" json:"domainname" yaml:"domainname"` + // If true, container images will be updated when a server starts if there // is an update available. If false the daemon will not attempt updates and will // defer to the host system to manage image updates. diff --git a/go.mod b/go.mod index 2a472a3..980e318 100644 --- a/go.mod +++ b/go.mod @@ -22,11 +22,14 @@ require ( github.com/beevik/etree v1.1.0 github.com/buger/jsonparser v0.0.0-20191204142016-1a29609e0929 github.com/cobaugh/osrelease v0.0.0-20181218015638-a93a0a55a249 + github.com/containerd/containerd v1.3.6 // indirect github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448 // indirect github.com/creasty/defaults v1.3.0 + github.com/docker/cli v17.12.1-ce-rc2+incompatible github.com/docker/distribution v2.7.1+incompatible // indirect - github.com/docker/docker v0.0.0-20180422163414-57142e89befe + github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible github.com/docker/go-connections v0.4.0 + github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.3.3 // indirect github.com/fatih/color v1.9.0 github.com/gabriel-vasile/mimetype v0.1.4 @@ -49,6 +52,7 @@ require ( github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/morikuni/aec v1.0.0 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/opencontainers/go-digest v1.0.0-rc1 // indirect github.com/opencontainers/image-spec v1.0.1 // indirect diff --git a/go.sum b/go.sum index dcbbc06..08fc502 100644 --- a/go.sum +++ b/go.sum @@ -35,12 +35,16 @@ github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +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/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/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cobaugh/osrelease v0.0.0-20181218015638-a93a0a55a249 h1:R0IDH8daQ3lODvu8YtxnIqqth5qMGCJyADoUQvmLx4o= github.com/cobaugh/osrelease v0.0.0-20181218015638-a93a0a55a249/go.mod h1:EHKW9yNEYSBpTKzuu7Y9oOrft/UlzH57rMIB03oev6M= +github.com/containerd/containerd v1.3.6 h1:SMfcKoQyWhaRsYq7290ioC6XFcHDNcHvcEMjF6ORpac= +github.com/containerd/containerd v1.3.6/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/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -56,12 +60,19 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/docker/cli v17.12.1-ce-rc2+incompatible h1:ESUycEAqvFuLglAHkUW66rCc2djYtd3i1x231svLq9o= +github.com/docker/cli v17.12.1-ce-rc2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v0.0.0-20180422163414-57142e89befe h1:VW8TnWi0CZgg7oCv0wH6evNwkzcJg/emnw4HrVIWws4= github.com/docker/docker v0.0.0-20180422163414-57142e89befe/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo= +github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible h1:iWPIG7pWIsCwT6ZtHnTUpoVMnete7O/pzd9HFE3+tn8= +github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= 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/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/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= @@ -112,6 +123,7 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb 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.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.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= @@ -152,6 +164,8 @@ github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2 github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= @@ -202,6 +216,7 @@ github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHX github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-shellwords v1.0.10 h1:Y7Xqm8piKOO3v10Thp7Z36h4FYFjt5xB//6XvOrs2Gw= github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= @@ -217,6 +232,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -252,12 +269,22 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/pterodactyl/sftp-server v1.1.1 h1:IjuOy21BNZxfejKnXG1RgLxXAYylDqBVpbKZ6+fG5FQ= github.com/pterodactyl/sftp-server v1.1.1/go.mod h1:b1VVWYv0RF9rxSZQqaD/rYXriiRMNPsbV//CKMXR4ag= @@ -383,6 +410,7 @@ golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/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-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -409,6 +437,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -450,8 +479,10 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/server/environment_docker.go b/server/environment_docker.go index d3e25f8..3fc30fa 100644 --- a/server/environment_docker.go +++ b/server/environment_docker.go @@ -95,9 +95,7 @@ func (d *DockerEnvironment) Exists() (bool, error) { // // @see docker/client/errors.go func (d *DockerEnvironment) IsRunning() (bool, error) { - ctx := context.Background() - - c, err := d.Client.ContainerInspect(ctx, d.Server.Uuid) + c, err := d.Client.ContainerInspect(context.Background(), d.Server.Uuid) if err != nil { return false, err } @@ -123,12 +121,14 @@ func (d *DockerEnvironment) InSituUpdate() error { return errors.WithStack(err) } - ctx, _ := context.WithTimeout(context.Background(), time.Second*10) u := container.UpdateConfig{ Resources: d.getResourcesForServer(), } d.Server.Log().WithField("limits", fmt.Sprintf("%+v", u.Resources)).Debug("updating server container on-the-fly with passed limits") + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() if _, err := d.Client.ContainerUpdate(ctx, d.Server.Uuid, u); err != nil { return errors.WithStack(err) } @@ -255,7 +255,9 @@ func (d *DockerEnvironment) Start() error { return errors.WithStack(err) } - ctx, _ := context.WithTimeout(context.Background(), time.Second*10) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + if err := d.Client.ContainerStart(ctx, d.Server.Uuid, types.ContainerStartOptions{}); err != nil { return errors.WithStack(err) } @@ -324,9 +326,7 @@ func (d *DockerEnvironment) WaitForStop(seconds int, terminate bool) error { // Forcefully terminates the container using the signal passed through. func (d *DockerEnvironment) Terminate(signal os.Signal) error { - ctx := context.Background() - - c, err := d.Client.ContainerInspect(ctx, d.Server.Uuid) + c, err := d.Client.ContainerInspect(context.Background(), d.Server.Uuid) if err != nil { return errors.WithStack(err) } @@ -338,19 +338,17 @@ func (d *DockerEnvironment) Terminate(signal os.Signal) error { d.Server.SetState(ProcessStoppingState) return d.Client.ContainerKill( - ctx, d.Server.Uuid, strings.TrimSuffix(strings.TrimPrefix(signal.String(), "signal "), "ed"), + context.Background(), d.Server.Uuid, strings.TrimSuffix(strings.TrimPrefix(signal.String(), "signal "), "ed"), ) } // Remove the Docker container from the machine. If the container is currently running // it will be forcibly stopped by Docker. func (d *DockerEnvironment) Destroy() error { - ctx := context.Background() - // Avoid crash detection firing off. d.Server.SetState(ProcessStoppingState) - err := d.Client.ContainerRemove(ctx, d.Server.Uuid, types.ContainerRemoveOptions{ + err := d.Client.ContainerRemove(context.Background(), d.Server.Uuid, types.ContainerRemoveOptions{ RemoveVolumes: true, RemoveLinks: false, Force: true, @@ -404,10 +402,8 @@ func (d *DockerEnvironment) Attach() error { return errors.WithStack(err) } - ctx := context.Background() - var err error - d.stream, err = d.Client.ContainerAttach(ctx, d.Server.Uuid, types.ContainerAttachOptions{ + d.stream, err = d.Client.ContainerAttach(context.Background(), d.Server.Uuid, types.ContainerAttachOptions{ Stdin: true, Stdout: true, Stderr: true, @@ -454,7 +450,6 @@ func (d *DockerEnvironment) FollowConsoleOutput() error { return errors.New(fmt.Sprintf("no such container: %s", d.Server.Uuid)) } - ctx := context.Background() opts := types.ContainerLogsOptions{ ShowStderr: true, ShowStdout: true, @@ -462,7 +457,7 @@ func (d *DockerEnvironment) FollowConsoleOutput() error { Since: time.Now().Format(time.RFC3339), } - reader, err := d.Client.ContainerLogs(ctx, d.Server.Uuid, opts) + reader, err := d.Client.ContainerLogs(context.Background(), d.Server.Uuid, opts) go func(r io.ReadCloser) { defer r.Close() @@ -488,9 +483,7 @@ func (d *DockerEnvironment) EnableResourcePolling() error { return errors.New("cannot enable resource polling on a server that is not running") } - ctx := context.Background() - - stats, err := d.Client.ContainerStats(ctx, d.Server.Uuid, true) + stats, err := d.Client.ContainerStats(context.Background(), d.Server.Uuid, true) if err != nil { return errors.WithStack(err) } @@ -564,15 +557,16 @@ func (d *DockerEnvironment) DisableResourcePolling() error { // correctly if anything. // // @todo handle authorization & local images -func (d *DockerEnvironment) ensureImageExists(c *client.Client) error { +func (d *DockerEnvironment) ensureImageExists() error { // Give it up to 15 minutes to pull the image. I think this should cover 99.8% of cases where an // image pull might fail. I can't imagine it will ever take more than 15 minutes to fully pull // an image. Let me know when I am inevitably wrong here... - ctx, _ := context.WithTimeout(context.Background(), time.Minute*15) + ctx, cancel := context.WithTimeout(context.Background(), time.Minute*15) + defer cancel() - out, err := c.ImagePull(ctx, d.Server.Container.Image, types.ImagePullOptions{All: false}) + out, err := d.Client.ImagePull(ctx, d.Server.Container.Image, types.ImagePullOptions{All: false}) if err != nil { - images, ierr := c.ImageList(ctx, types.ImageListOptions{}) + images, ierr := d.Client.ImageList(ctx, types.ImageListOptions{}) if ierr != nil { // Well damn, something has gone really wrong here, just go ahead and abort there // isn't much anything we can do to try and self-recover from this. @@ -581,16 +575,18 @@ func (d *DockerEnvironment) ensureImageExists(c *client.Client) error { for _, img := range images { for _, t := range img.RepoTags { - if t == d.Server.Container.Image { - d.Server.Log().WithFields(log.Fields{ - "image": d.Server.Container.Image, - "error": errors.New(err.Error()), - }).Warn("unable to pull requested image from remote source, however the image exists locally") - - // Okay, we found a matching container image, in that case just go ahead and return - // from this function, since there is nothing else we need to do here. - return nil + if t != d.Server.Container.Image { + continue } + + d.Server.Log().WithFields(log.Fields{ + "image": d.Server.Container.Image, + "error": errors.New(err.Error()), + }).Warn("unable to pull requested image from remote source, however the image exists locally") + + // Okay, we found a matching container image, in that case just go ahead and return + // from this function, since there is nothing else we need to do here. + return nil } } @@ -617,12 +613,6 @@ func (d *DockerEnvironment) ensureImageExists(c *client.Client) error { // Creates a new container for the server using all of the data that is currently // available for it. If the container already exists it will be returned. func (d *DockerEnvironment) Create() error { - ctx := context.Background() - cli, err := client.NewClientWithOpts(client.FromEnv) - if err != nil { - return errors.WithStack(err) - } - // Ensure the data directory exists before getting too far through this process. if err := d.Server.Filesystem.EnsureDataDirectory(); err != nil { return errors.WithStack(err) @@ -631,19 +621,20 @@ func (d *DockerEnvironment) Create() error { // If the container already exists don't hit the user with an error, just return // the current information about it which is what we would do when creating the // container anyways. - if _, err := cli.ContainerInspect(ctx, d.Server.Uuid); err == nil { + if _, err := d.Client.ContainerInspect(context.Background(), d.Server.Uuid); err == nil { return nil } else if !client.IsErrNotFound(err) { return errors.WithStack(err) } // Try to pull the requested image before creating the container. - if err := d.ensureImageExists(cli); err != nil { + if err := d.ensureImageExists(); err != nil { return errors.WithStack(err) } conf := &container.Config{ - Hostname: "container", + Hostname: d.Server.Uuid, + Domainname: config.Get().Docker.Domainname, User: strconv.Itoa(config.Get().System.User.Uid), AttachStdin: true, AttachStdout: true, @@ -747,7 +738,7 @@ func (d *DockerEnvironment) Create() error { NetworkMode: container.NetworkMode(config.Get().Docker.Network.Mode), } - if _, err := cli.ContainerCreate(ctx, conf, hostConf, nil, d.Server.Uuid); err != nil { + if _, err := d.Client.ContainerCreate(context.Background(), conf, hostConf, nil, d.Server.Uuid); err != nil { return errors.WithStack(err) } @@ -769,9 +760,7 @@ func (d *DockerEnvironment) SendCommand(c string) error { // Reads the log file for the server. This does not care if the server is running or not, it will // simply try to read the last X bytes of the file and return them. func (d *DockerEnvironment) Readlog(len int64) ([]string, error) { - ctx := context.Background() - - j, err := d.Client.ContainerInspect(ctx, d.Server.Uuid) + j, err := d.Client.ContainerInspect(context.Background(), d.Server.Uuid) if err != nil { return nil, err } diff --git a/system/const.go b/system/const.go index 435b270..87d87fb 100644 --- a/system/const.go +++ b/system/const.go @@ -1,6 +1,6 @@ package system -const ( +var ( // The current version of this software. Version = "0.0.1" )