Code cleanup for remote client
This commit is contained in:
parent
e3b0b91912
commit
065da77afa
|
@ -137,11 +137,12 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
|
||||||
"gid": config.Get().System.User.Gid,
|
"gid": config.Get().System.User.Gid,
|
||||||
}).Info("configured system user successfully")
|
}).Info("configured system user successfully")
|
||||||
|
|
||||||
pclient := remote.CreateClient(
|
pclient := remote.New(
|
||||||
config.Get().PanelLocation,
|
config.Get().PanelLocation,
|
||||||
config.Get().AuthenticationTokenId,
|
remote.WithCredentials(config.Get().AuthenticationTokenId, config.Get().AuthenticationToken),
|
||||||
config.Get().AuthenticationToken,
|
remote.WithHttpClient(&http.Client{
|
||||||
remote.WithTimeout(time.Second*time.Duration(config.Get().RemoteQuery.Timeout)),
|
Timeout: time.Second * time.Duration(config.Get().RemoteQuery.Timeout),
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
manager, err := server.NewManager(cmd.Context(), pclient)
|
manager, err := server.NewManager(cmd.Context(), pclient)
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
package remote
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *client) GetBackupRemoteUploadURLs(ctx context.Context, backup string, size int64) (BackupRemoteUploadResponse, error) {
|
|
||||||
var data BackupRemoteUploadResponse
|
|
||||||
res, err := c.get(ctx, fmt.Sprintf("/backups/%s", backup), q{"size": strconv.FormatInt(size, 10)})
|
|
||||||
if err != nil {
|
|
||||||
return data, err
|
|
||||||
}
|
|
||||||
defer res.Body.Close()
|
|
||||||
|
|
||||||
if res.HasError() {
|
|
||||||
return data, res.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
err = res.BindJSON(&data)
|
|
||||||
return data, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) SetBackupStatus(ctx context.Context, backup string, data BackupRequest) error {
|
|
||||||
resp, err := c.post(ctx, fmt.Sprintf("/backups/%s", backup), data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
return resp.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// SendRestorationStatus triggers a request to the Panel to notify it that a
|
|
||||||
// restoration has been completed and the server should be marked as being
|
|
||||||
// activated again.
|
|
||||||
func (c *client) SendRestorationStatus(ctx context.Context, backup string, successful bool) error {
|
|
||||||
resp, err := c.post(ctx, fmt.Sprintf("/backups/%s/restore", backup), d{"successful": successful})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
return resp.Error()
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
package remote
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Client interface {
|
|
||||||
GetBackupRemoteUploadURLs(ctx context.Context, backup string, size int64) (BackupRemoteUploadResponse, error)
|
|
||||||
GetInstallationScript(ctx context.Context, uuid string) (InstallationScript, error)
|
|
||||||
GetServerConfiguration(ctx context.Context, uuid string) (ServerConfigurationResponse, error)
|
|
||||||
GetServers(context context.Context, perPage int) ([]RawServerData, error)
|
|
||||||
SetArchiveStatus(ctx context.Context, uuid string, successful bool) error
|
|
||||||
SetBackupStatus(ctx context.Context, backup string, data BackupRequest) error
|
|
||||||
SendRestorationStatus(ctx context.Context, backup string, successful bool) error
|
|
||||||
SetInstallationStatus(ctx context.Context, uuid string, successful bool) error
|
|
||||||
SetTransferStatus(ctx context.Context, uuid string, successful bool) error
|
|
||||||
ValidateSftpCredentials(ctx context.Context, request SftpAuthRequest) (SftpAuthResponse, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type client struct {
|
|
||||||
httpClient *http.Client
|
|
||||||
baseUrl string
|
|
||||||
tokenId string
|
|
||||||
token string
|
|
||||||
retries int
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClientOption func(c *client)
|
|
||||||
|
|
||||||
func CreateClient(base, tokenId, token string, opts ...ClientOption) Client {
|
|
||||||
httpClient := &http.Client{
|
|
||||||
Timeout: time.Second * 15,
|
|
||||||
}
|
|
||||||
base = strings.TrimSuffix(base, "/")
|
|
||||||
c := &client{
|
|
||||||
baseUrl: base + "/api/remote",
|
|
||||||
tokenId: tokenId,
|
|
||||||
token: token,
|
|
||||||
httpClient: httpClient,
|
|
||||||
retries: 3,
|
|
||||||
}
|
|
||||||
for _, o := range opts {
|
|
||||||
o(c)
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithTimeout(timeout time.Duration) ClientOption {
|
|
||||||
return func(c *client) {
|
|
||||||
c.httpClient.Timeout = timeout
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package remote
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
)
|
|
||||||
|
|
||||||
func createTestClient(h http.HandlerFunc) (*client, *httptest.Server) {
|
|
||||||
s := httptest.NewServer(h)
|
|
||||||
c := &client{
|
|
||||||
httpClient: s.Client(),
|
|
||||||
baseUrl: s.URL,
|
|
||||||
|
|
||||||
retries: 1,
|
|
||||||
tokenId: "testid",
|
|
||||||
token: "testtoken",
|
|
||||||
}
|
|
||||||
return c, s
|
|
||||||
}
|
|
105
remote/http.go
105
remote/http.go
|
@ -9,34 +9,65 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/apex/log"
|
||||||
"github.com/pterodactyl/wings/system"
|
"github.com/pterodactyl/wings/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A generic type allowing for easy binding use when making requests to API
|
type Client interface {
|
||||||
// endpoints that only expect a singular argument or something that would not
|
GetBackupRemoteUploadURLs(ctx context.Context, backup string, size int64) (BackupRemoteUploadResponse, error)
|
||||||
// benefit from being a typed struct.
|
GetInstallationScript(ctx context.Context, uuid string) (InstallationScript, error)
|
||||||
//
|
GetServerConfiguration(ctx context.Context, uuid string) (ServerConfigurationResponse, error)
|
||||||
// Inspired by gin.H, same concept.
|
GetServers(context context.Context, perPage int) ([]RawServerData, error)
|
||||||
type d map[string]interface{}
|
SetArchiveStatus(ctx context.Context, uuid string, successful bool) error
|
||||||
|
SetBackupStatus(ctx context.Context, backup string, data BackupRequest) error
|
||||||
// Same concept as d, but a map of strings, used for querying GET requests.
|
SendRestorationStatus(ctx context.Context, backup string, successful bool) error
|
||||||
type q map[string]string
|
SetInstallationStatus(ctx context.Context, uuid string, successful bool) error
|
||||||
|
SetTransferStatus(ctx context.Context, uuid string, successful bool) error
|
||||||
// Response is a custom response type that allows for commonly used error
|
ValidateSftpCredentials(ctx context.Context, request SftpAuthRequest) (SftpAuthResponse, error)
|
||||||
// handling and response parsing from the Panel API. This just embeds the normal
|
|
||||||
// HTTP response from Go and we attach a few helper functions to it.
|
|
||||||
type Response struct {
|
|
||||||
*http.Response
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Pagination struct {
|
type client struct {
|
||||||
CurrentPage uint `json:"current_page"`
|
httpClient *http.Client
|
||||||
From uint `json:"from"`
|
baseUrl string
|
||||||
LastPage uint `json:"last_page"`
|
tokenId string
|
||||||
PerPage uint `json:"per_page"`
|
token string
|
||||||
To uint `json:"to"`
|
retries int
|
||||||
Total uint `json:"total"`
|
}
|
||||||
|
|
||||||
|
// New returns a new HTTP request client that is used for making authenticated
|
||||||
|
// requests to the Panel that this instance is running under.
|
||||||
|
func New(base string, opts ...ClientOption) Client {
|
||||||
|
c := client{
|
||||||
|
baseUrl: strings.TrimSuffix(base, "/") + "/api/remote",
|
||||||
|
httpClient: &http.Client{
|
||||||
|
Timeout: time.Second * 15,
|
||||||
|
},
|
||||||
|
retries: 3,
|
||||||
|
}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&c)
|
||||||
|
}
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCredentials sets the credentials to use when making request to the remote
|
||||||
|
// API endpoint.
|
||||||
|
func WithCredentials(id, token string) ClientOption {
|
||||||
|
return func(c *client) {
|
||||||
|
c.tokenId = id
|
||||||
|
c.token = token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithHttpClient sets the underlying HTTP client instance to use when making
|
||||||
|
// requests to the Panel API.
|
||||||
|
func WithHttpClient(httpClient *http.Client) ClientOption {
|
||||||
|
return func(c *client) {
|
||||||
|
c.httpClient = httpClient
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// requestOnce creates a http request and executes it once. Prefer request()
|
// requestOnce creates a http request and executes it once. Prefer request()
|
||||||
|
@ -103,6 +134,13 @@ func (c *client) post(ctx context.Context, path string, data interface{}) (*Resp
|
||||||
return c.request(ctx, http.MethodPost, path, bytes.NewBuffer(b))
|
return c.request(ctx, http.MethodPost, path, bytes.NewBuffer(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Response is a custom response type that allows for commonly used error
|
||||||
|
// handling and response parsing from the Panel API. This just embeds the normal
|
||||||
|
// HTTP response from Go and we attach a few helper functions to it.
|
||||||
|
type Response struct {
|
||||||
|
*http.Response
|
||||||
|
}
|
||||||
|
|
||||||
// HasError determines if the API call encountered an error. If no request has
|
// HasError determines if the API call encountered an error. If no request has
|
||||||
// been made the response will be false. This function will evaluate to true if
|
// been made the response will be false. This function will evaluate to true if
|
||||||
// the response code is anything 300 or higher.
|
// the response code is anything 300 or higher.
|
||||||
|
@ -165,3 +203,26 @@ func (r *Response) Error() error {
|
||||||
|
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logs the request into the debug log with all of the important request bits.
|
||||||
|
// The authorization key will be cleaned up before being output.
|
||||||
|
func debugLogRequest(req *http.Request) {
|
||||||
|
if l, ok := log.Log.(*log.Logger); ok && l.Level != log.DebugLevel {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
headers := make(map[string][]string)
|
||||||
|
for k, v := range req.Header {
|
||||||
|
if k != "Authorization" || len(v) == 0 || len(v[0]) == 0 {
|
||||||
|
headers[k] = v
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
headers[k] = []string{"(redacted)"}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"method": req.Method,
|
||||||
|
"endpoint": req.URL.String(),
|
||||||
|
"headers": headers,
|
||||||
|
}).Debug("making request to external HTTP endpoint")
|
||||||
|
}
|
||||||
|
|
|
@ -3,11 +3,25 @@ package remote
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func createTestClient(h http.HandlerFunc) (*client, *httptest.Server) {
|
||||||
|
s := httptest.NewServer(h)
|
||||||
|
c := &client{
|
||||||
|
httpClient: s.Client(),
|
||||||
|
baseUrl: s.URL,
|
||||||
|
|
||||||
|
retries: 1,
|
||||||
|
tokenId: "testid",
|
||||||
|
token: "testtoken",
|
||||||
|
}
|
||||||
|
return c, s
|
||||||
|
}
|
||||||
|
|
||||||
func TestRequest(t *testing.T) {
|
func TestRequest(t *testing.T) {
|
||||||
c, _ := createTestClient(func(rw http.ResponseWriter, r *http.Request) {
|
c, _ := createTestClient(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
assert.Equal(t, "application/vnd.pterodactyl.v1+json", r.Header.Get("Accept"))
|
assert.Equal(t, "application/vnd.pterodactyl.v1+json", r.Header.Get("Accept"))
|
||||||
|
|
|
@ -144,6 +144,44 @@ func (c *client) ValidateSftpCredentials(ctx context.Context, request SftpAuthRe
|
||||||
return auth, err
|
return auth, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *client) GetBackupRemoteUploadURLs(ctx context.Context, backup string, size int64) (BackupRemoteUploadResponse, error) {
|
||||||
|
var data BackupRemoteUploadResponse
|
||||||
|
res, err := c.get(ctx, fmt.Sprintf("/backups/%s", backup), q{"size": strconv.FormatInt(size, 10)})
|
||||||
|
if err != nil {
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
if res.HasError() {
|
||||||
|
return data, res.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = res.BindJSON(&data)
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) SetBackupStatus(ctx context.Context, backup string, data BackupRequest) error {
|
||||||
|
resp, err := c.post(ctx, fmt.Sprintf("/backups/%s", backup), data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
return resp.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// SendRestorationStatus triggers a request to the Panel to notify it that a
|
||||||
|
// restoration has been completed and the server should be marked as being
|
||||||
|
// activated again.
|
||||||
|
func (c *client) SendRestorationStatus(ctx context.Context, backup string, successful bool) error {
|
||||||
|
resp, err := c.post(ctx, fmt.Sprintf("/backups/%s/restore", backup), d{"successful": successful})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
return resp.Error()
|
||||||
|
}
|
||||||
|
|
||||||
// getServersPaged returns a subset of servers from the Panel API using the
|
// getServersPaged returns a subset of servers from the Panel API using the
|
||||||
// pagination query parameters.
|
// pagination query parameters.
|
||||||
func (c *client) getServersPaged(ctx context.Context, page, limit int) ([]RawServerData, Pagination, error) {
|
func (c *client) getServersPaged(ctx context.Context, page, limit int) ([]RawServerData, Pagination, error) {
|
||||||
|
|
|
@ -9,6 +9,27 @@ import (
|
||||||
"github.com/pterodactyl/wings/parser"
|
"github.com/pterodactyl/wings/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// A generic type allowing for easy binding use when making requests to API
|
||||||
|
// endpoints that only expect a singular argument or something that would not
|
||||||
|
// benefit from being a typed struct.
|
||||||
|
//
|
||||||
|
// Inspired by gin.H, same concept.
|
||||||
|
type d map[string]interface{}
|
||||||
|
|
||||||
|
// Same concept as d, but a map of strings, used for querying GET requests.
|
||||||
|
type q map[string]string
|
||||||
|
|
||||||
|
type ClientOption func(c *client)
|
||||||
|
|
||||||
|
type Pagination struct {
|
||||||
|
CurrentPage uint `json:"current_page"`
|
||||||
|
From uint `json:"from"`
|
||||||
|
LastPage uint `json:"last_page"`
|
||||||
|
PerPage uint `json:"per_page"`
|
||||||
|
To uint `json:"to"`
|
||||||
|
Total uint `json:"total"`
|
||||||
|
}
|
||||||
|
|
||||||
// ServerConfigurationResponse holds the server configuration data returned from
|
// ServerConfigurationResponse holds the server configuration data returned from
|
||||||
// the Panel. When a server process is started, Wings communicates with the
|
// the Panel. When a server process is started, Wings communicates with the
|
||||||
// Panel to fetch the latest build information as well as get all of the details
|
// Panel to fetch the latest build information as well as get all of the details
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
package remote
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/apex/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Logs the request into the debug log with all of the important request bits.
|
|
||||||
// The authorization key will be cleaned up before being output.
|
|
||||||
func debugLogRequest(req *http.Request) {
|
|
||||||
if l, ok := log.Log.(*log.Logger); ok && l.Level != log.DebugLevel {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
headers := make(map[string][]string)
|
|
||||||
for k, v := range req.Header {
|
|
||||||
if k != "Authorization" || len(v) == 0 || len(v[0]) == 0 {
|
|
||||||
headers[k] = v
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
headers[k] = []string{"(redacted)"}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"method": req.Method,
|
|
||||||
"endpoint": req.URL.String(),
|
|
||||||
"headers": headers,
|
|
||||||
}).Debug("making request to external HTTP endpoint")
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user