TUN-10247: Update tail command to use /management/logs endpoint
* TUN-10247: Update tail command to use /management/logs endpoint The /management endpoint will be deprecated in favor of new /management/resource endpoints. Because of that, we'll need cloudflared to use the new endpoint. Closes TUN-10247
This commit is contained in:
parent
a0bcbf6a44
commit
059f4d9898
|
|
@ -8,7 +8,7 @@ type TunnelClient interface {
|
||||||
CreateTunnel(name string, tunnelSecret []byte) (*TunnelWithToken, error)
|
CreateTunnel(name string, tunnelSecret []byte) (*TunnelWithToken, error)
|
||||||
GetTunnel(tunnelID uuid.UUID) (*Tunnel, error)
|
GetTunnel(tunnelID uuid.UUID) (*Tunnel, error)
|
||||||
GetTunnelToken(tunnelID uuid.UUID) (string, error)
|
GetTunnelToken(tunnelID uuid.UUID) (string, error)
|
||||||
GetManagementToken(tunnelID uuid.UUID) (string, error)
|
GetManagementToken(tunnelID uuid.UUID, resource ManagementResource) (string, error)
|
||||||
DeleteTunnel(tunnelID uuid.UUID, cascade bool) error
|
DeleteTunnel(tunnelID uuid.UUID, cascade bool) error
|
||||||
ListTunnels(filter *TunnelFilter) ([]*Tunnel, error)
|
ListTunnels(filter *TunnelFilter) ([]*Tunnel, error)
|
||||||
ListActiveClients(tunnelID uuid.UUID) ([]*ActiveClient, error)
|
ListActiveClients(tunnelID uuid.UUID) ([]*ActiveClient, error)
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,21 @@ import (
|
||||||
|
|
||||||
var ErrTunnelNameConflict = errors.New("tunnel with name already exists")
|
var ErrTunnelNameConflict = errors.New("tunnel with name already exists")
|
||||||
|
|
||||||
|
type ManagementResource int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Logs ManagementResource = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r ManagementResource) String() string {
|
||||||
|
switch r {
|
||||||
|
case Logs:
|
||||||
|
return "logs"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Tunnel struct {
|
type Tunnel struct {
|
||||||
ID uuid.UUID `json:"id"`
|
ID uuid.UUID `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
@ -50,10 +65,6 @@ type newTunnel struct {
|
||||||
TunnelSecret []byte `json:"tunnel_secret"`
|
TunnelSecret []byte `json:"tunnel_secret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type managementRequest struct {
|
|
||||||
Resources []string `json:"resources"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CleanupParams struct {
|
type CleanupParams struct {
|
||||||
queryParams url.Values
|
queryParams url.Values
|
||||||
}
|
}
|
||||||
|
|
@ -137,15 +148,16 @@ func (r *RESTClient) GetTunnelToken(tunnelID uuid.UUID) (token string, err error
|
||||||
return "", r.statusCodeToError("get tunnel token", resp)
|
return "", r.statusCodeToError("get tunnel token", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RESTClient) GetManagementToken(tunnelID uuid.UUID) (token string, err error) {
|
// managementEndpointPath returns the path segment for a management resource endpoint
|
||||||
endpoint := r.baseEndpoints.accountLevel
|
func managementEndpointPath(tunnelID uuid.UUID, res ManagementResource) string {
|
||||||
endpoint.Path = path.Join(endpoint.Path, fmt.Sprintf("%v/management", tunnelID))
|
return fmt.Sprintf("%v/management/%s", tunnelID, res.String())
|
||||||
|
|
||||||
body := &managementRequest{
|
|
||||||
Resources: []string{"logs"},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := r.sendRequest("POST", endpoint, body)
|
func (r *RESTClient) GetManagementToken(tunnelID uuid.UUID, res ManagementResource) (token string, err error) {
|
||||||
|
endpoint := r.baseEndpoints.accountLevel
|
||||||
|
endpoint.Path = path.Join(endpoint.Path, managementEndpointPath(tunnelID, res))
|
||||||
|
|
||||||
|
resp, err := r.sendRequest("POST", endpoint, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrap(err, "REST request failed")
|
return "", errors.Wrap(err, "REST request failed")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package cfapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -11,6 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
var loc, _ = time.LoadLocation("UTC")
|
var loc, _ = time.LoadLocation("UTC")
|
||||||
|
|
@ -52,7 +52,6 @@ func Test_unmarshalTunnel(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalTunnelOk(t *testing.T) {
|
func TestUnmarshalTunnelOk(t *testing.T) {
|
||||||
|
|
||||||
jsonBody := `{"success": true, "result": {"id": "00000000-0000-0000-0000-000000000000","name":"test","created_at":"0001-01-01T00:00:00Z","connections":[]}}`
|
jsonBody := `{"success": true, "result": {"id": "00000000-0000-0000-0000-000000000000","name":"test","created_at":"0001-01-01T00:00:00Z","connections":[]}}`
|
||||||
expected := Tunnel{
|
expected := Tunnel{
|
||||||
ID: uuid.Nil,
|
ID: uuid.Nil,
|
||||||
|
|
@ -61,12 +60,11 @@ func TestUnmarshalTunnelOk(t *testing.T) {
|
||||||
Connections: []Connection{},
|
Connections: []Connection{},
|
||||||
}
|
}
|
||||||
actual, err := unmarshalTunnel(bytes.NewReader([]byte(jsonBody)))
|
actual, err := unmarshalTunnel(bytes.NewReader([]byte(jsonBody)))
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, &expected, actual)
|
require.Equal(t, &expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalTunnelErr(t *testing.T) {
|
func TestUnmarshalTunnelErr(t *testing.T) {
|
||||||
|
|
||||||
tests := []string{
|
tests := []string{
|
||||||
`abc`,
|
`abc`,
|
||||||
`{"success": true, "result": abc}`,
|
`{"success": true, "result": abc}`,
|
||||||
|
|
@ -76,7 +74,53 @@ func TestUnmarshalTunnelErr(t *testing.T) {
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
_, err := unmarshalTunnel(bytes.NewReader([]byte(test)))
|
_, err := unmarshalTunnel(bytes.NewReader([]byte(test)))
|
||||||
assert.Error(t, err, fmt.Sprintf("Test #%v failed", i))
|
assert.Error(t, err, "Test #%v failed", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManagementResource_String(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
resource ManagementResource
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Logs",
|
||||||
|
resource: Logs,
|
||||||
|
want: "logs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tt.want, tt.resource.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManagementResource_String_Unknown(t *testing.T) {
|
||||||
|
unknown := ManagementResource(999)
|
||||||
|
assert.Equal(t, "", unknown.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManagementEndpointPath(t *testing.T) {
|
||||||
|
tunnelID := uuid.MustParse("b34cc7ce-925b-46ee-bc23-4cb5c18d8292")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
resource ManagementResource
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Logs resource",
|
||||||
|
resource: Logs,
|
||||||
|
want: "b34cc7ce-925b-46ee-bc23-4cb5c18d8292/management/logs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := managementEndpointPath(tunnelID, tt.resource)
|
||||||
|
assert.Equal(t, tt.want, got)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,6 +141,6 @@ func TestUnmarshalConnections(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
actual, err := parseConnectionsDetails(bytes.NewReader([]byte(jsonBody)))
|
actual, err := parseConnectionsDetails(bytes.NewReader([]byte(jsonBody)))
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, []*ActiveClient{&expected}, actual)
|
assert.Equal(t, []*ActiveClient{&expected}, actual)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ import (
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"nhooyr.io/websocket"
|
"nhooyr.io/websocket"
|
||||||
|
|
||||||
|
"github.com/cloudflare/cloudflared/cfapi"
|
||||||
|
|
||||||
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
|
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
|
||||||
cfdflags "github.com/cloudflare/cloudflared/cmd/cloudflared/flags"
|
cfdflags "github.com/cloudflare/cloudflared/cmd/cloudflared/flags"
|
||||||
"github.com/cloudflare/cloudflared/credentials"
|
"github.com/cloudflare/cloudflared/credentials"
|
||||||
|
|
@ -52,7 +54,7 @@ func buildTailManagementTokenSubcommand() *cli.Command {
|
||||||
func managementTokenCommand(c *cli.Context) error {
|
func managementTokenCommand(c *cli.Context) error {
|
||||||
log := createLogger(c)
|
log := createLogger(c)
|
||||||
|
|
||||||
token, err := getManagementToken(c, log)
|
token, err := getManagementToken(c, log, cfapi.Logs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -231,7 +233,7 @@ func parseFilters(c *cli.Context) (*management.StreamingFilters, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getManagementToken will make a call to the Cloudflare API to acquire a management token for the requested tunnel.
|
// getManagementToken will make a call to the Cloudflare API to acquire a management token for the requested tunnel.
|
||||||
func getManagementToken(c *cli.Context, log *zerolog.Logger) (string, error) {
|
func getManagementToken(c *cli.Context, log *zerolog.Logger, res cfapi.ManagementResource) (string, error) {
|
||||||
userCreds, err := credentials.Read(c.String(cfdflags.OriginCert), log)
|
userCreds, err := credentials.Read(c.String(cfdflags.OriginCert), log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
@ -258,7 +260,7 @@ func getManagementToken(c *cli.Context, log *zerolog.Logger) (string, error) {
|
||||||
return "", errors.New("unable to parse provided tunnel id as a valid UUID")
|
return "", errors.New("unable to parse provided tunnel id as a valid UUID")
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := client.GetManagementToken(tunnelID)
|
token, err := client.GetManagementToken(tunnelID, res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
@ -267,12 +269,12 @@ func getManagementToken(c *cli.Context, log *zerolog.Logger) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildURL will build the management url to contain the required query parameters to authenticate the request.
|
// buildURL will build the management url to contain the required query parameters to authenticate the request.
|
||||||
func buildURL(c *cli.Context, log *zerolog.Logger) (url.URL, error) {
|
func buildURL(c *cli.Context, log *zerolog.Logger, res cfapi.ManagementResource) (url.URL, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
token := c.String("token")
|
token := c.String("token")
|
||||||
if token == "" {
|
if token == "" {
|
||||||
token, err = getManagementToken(c, log)
|
token, err = getManagementToken(c, log, res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return url.URL{}, fmt.Errorf("unable to acquire management token for requested tunnel id: %w", err)
|
return url.URL{}, fmt.Errorf("unable to acquire management token for requested tunnel id: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -345,7 +347,7 @@ func Run(c *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := buildURL(c, log)
|
u, err := buildURL(c, log, cfapi.Logs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Err(err).Msg("unable to construct management request URL")
|
log.Err(err).Msg("unable to construct management request URL")
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue