TUN-3998: Allow to cleanup the connections of a tunnel limited to a single client

This commit is contained in:
Nuno Diegues 2021-03-19 23:26:51 +00:00
parent 9767ba1853
commit 4a7763e497
5 changed files with 52 additions and 7 deletions

View File

@ -224,7 +224,7 @@ func (sc *subcommandContext) delete(tunnelIDs []uuid.UUID) error {
return fmt.Errorf("Tunnel %s has already been deleted", tunnel.ID) return fmt.Errorf("Tunnel %s has already been deleted", tunnel.ID)
} }
if forceFlagSet { if forceFlagSet {
if err := client.CleanupConnections(tunnel.ID); err != nil { if err := client.CleanupConnections(tunnel.ID, tunnelstore.NewCleanupParams()); err != nil {
return errors.Wrapf(err, "Error cleaning up connections for tunnel %s", tunnel.ID) return errors.Wrapf(err, "Error cleaning up connections for tunnel %s", tunnel.ID)
} }
} }
@ -276,13 +276,24 @@ func (sc *subcommandContext) run(tunnelID uuid.UUID) error {
} }
func (sc *subcommandContext) cleanupConnections(tunnelIDs []uuid.UUID) error { func (sc *subcommandContext) cleanupConnections(tunnelIDs []uuid.UUID) error {
params := tunnelstore.NewCleanupParams()
extraLog := ""
if connector := sc.c.String("connector-id"); connector != "" {
connectorID, err := uuid.Parse(connector)
if err != nil {
return errors.Wrapf(err, "%s is not a valid client ID (must be a UUID)", connector)
}
params.ForClient(connectorID)
extraLog = fmt.Sprintf(" for connector-id %s", connectorID.String())
}
client, err := sc.client() client, err := sc.client()
if err != nil { if err != nil {
return err return err
} }
for _, tunnelID := range tunnelIDs { for _, tunnelID := range tunnelIDs {
sc.log.Info().Msgf("Cleanup connection for tunnel %s", tunnelID) sc.log.Info().Msgf("Cleanup connection for tunnel %s%s", tunnelID, extraLog)
if err := client.CleanupConnections(tunnelID); err != nil { if err := client.CleanupConnections(tunnelID, params); err != nil {
sc.log.Error().Msgf("Error cleaning up connections for tunnel %v, error :%v", tunnelID, err) sc.log.Error().Msgf("Error cleaning up connections for tunnel %v, error :%v", tunnelID, err)
} }
} }

View File

@ -4,11 +4,12 @@ import (
"encoding/base64" "encoding/base64"
"flag" "flag"
"fmt" "fmt"
"github.com/rs/zerolog"
"reflect" "reflect"
"testing" "testing"
"time" "time"
"github.com/rs/zerolog"
"github.com/cloudflare/cloudflared/connection" "github.com/cloudflare/cloudflared/connection"
"github.com/cloudflare/cloudflared/tunnelstore" "github.com/cloudflare/cloudflared/tunnelstore"
"github.com/google/uuid" "github.com/google/uuid"
@ -260,7 +261,7 @@ func (d *deleteMockTunnelStore) DeleteTunnel(tunnelID uuid.UUID) error {
return nil return nil
} }
func (d *deleteMockTunnelStore) CleanupConnections(tunnelID uuid.UUID) error { func (d *deleteMockTunnelStore) CleanupConnections(tunnelID uuid.UUID, _ *tunnelstore.CleanupParams) error {
tunnel, ok := d.mockTunnels[tunnelID] tunnel, ok := d.mockTunnels[tunnelID]
if !ok { if !ok {
return fmt.Errorf("Couldn't find tunnel: %v", tunnelID) return fmt.Errorf("Couldn't find tunnel: %v", tunnelID)

View File

@ -126,6 +126,12 @@ var (
Usage: "Inverts the sort order of the tunnel info.", Usage: "Inverts the sort order of the tunnel info.",
EnvVars: []string{"TUNNEL_INFO_INVERT_SORT"}, EnvVars: []string{"TUNNEL_INFO_INVERT_SORT"},
} }
cleanupClientFlag = &cli.StringFlag{
Name: "connector-id",
Aliases: []string{"c"},
Usage: `Constraints the cleanup to stop the connections of a single Connector (by its ID). You can find the various Connectors (and their IDs) currently connected to your tunnel via 'cloudflared tunnel info <name>'.`,
EnvVars: []string{"TUNNEL_CLEANUP_CONNECTOR"},
}
) )
func buildCreateCommand() *cli.Command { func buildCreateCommand() *cli.Command {
@ -600,6 +606,7 @@ func buildCleanupCommand() *cli.Command {
Usage: "Cleanup tunnel connections", Usage: "Cleanup tunnel connections",
UsageText: "cloudflared tunnel [tunnel command options] cleanup [subcommand options] TUNNEL", UsageText: "cloudflared tunnel [tunnel command options] cleanup [subcommand options] TUNNEL",
Description: "Delete connections for tunnels with the given UUIDs or names.", Description: "Delete connections for tunnels with the given UUIDs or names.",
Flags: []cli.Flag{cleanupClientFlag},
CustomHelpTemplate: commandHelpTemplate(), CustomHelpTemplate: commandHelpTemplate(),
} }
} }

View File

@ -0,0 +1,25 @@
package tunnelstore
import (
"net/url"
"github.com/google/uuid"
)
type CleanupParams struct {
queryParams url.Values
}
func NewCleanupParams() *CleanupParams {
return &CleanupParams{
queryParams: url.Values{},
}
}
func (cp *CleanupParams) ForClient(clientID uuid.UUID) {
cp.queryParams.Set("client_id", clientID.String())
}
func (cp CleanupParams) encode() string {
return cp.queryParams.Encode()
}

View File

@ -204,7 +204,7 @@ type Client interface {
DeleteTunnel(tunnelID uuid.UUID) error DeleteTunnel(tunnelID uuid.UUID) error
ListTunnels(filter *Filter) ([]*Tunnel, error) ListTunnels(filter *Filter) ([]*Tunnel, error)
ListActiveClients(tunnelID uuid.UUID) ([]*ActiveClient, error) ListActiveClients(tunnelID uuid.UUID) ([]*ActiveClient, error)
CleanupConnections(tunnelID uuid.UUID) error CleanupConnections(tunnelID uuid.UUID, params *CleanupParams) error
RouteTunnel(tunnelID uuid.UUID, route Route) (RouteResult, error) RouteTunnel(tunnelID uuid.UUID, route Route) (RouteResult, error)
// Teamnet endpoints // Teamnet endpoints
@ -370,8 +370,9 @@ func parseConnectionsDetails(reader io.Reader) ([]*ActiveClient, error) {
return clients, err return clients, err
} }
func (r *RESTClient) CleanupConnections(tunnelID uuid.UUID) error { func (r *RESTClient) CleanupConnections(tunnelID uuid.UUID, params *CleanupParams) error {
endpoint := r.baseEndpoints.accountLevel endpoint := r.baseEndpoints.accountLevel
endpoint.RawQuery = params.encode()
endpoint.Path = path.Join(endpoint.Path, fmt.Sprintf("%v/connections", tunnelID)) endpoint.Path = path.Join(endpoint.Path, fmt.Sprintf("%v/connections", tunnelID))
resp, err := r.sendRequest("DELETE", endpoint, nil) resp, err := r.sendRequest("DELETE", endpoint, nil)
if err != nil { if err != nil {