cloudflared-mirror/cmd/cloudflared/tunnel/subcommand_context_test.go

398 lines
10 KiB
Go

package tunnel
import (
"encoding/base64"
"flag"
"fmt"
"reflect"
"testing"
"time"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/rs/zerolog"
"github.com/urfave/cli/v2"
"github.com/cloudflare/cloudflared/connection"
"github.com/cloudflare/cloudflared/tunnelstore"
)
func Test_findIDs(t *testing.T) {
type args struct {
tunnels []*tunnelstore.Tunnel
inputs []string
}
tests := []struct {
name string
args args
want []uuid.UUID
wantErr bool
}{
{
name: "input not found",
args: args{
inputs: []string{"asdf"},
},
wantErr: true,
},
{
name: "only UUID",
args: args{
inputs: []string{"a8398a0b-876d-48ed-b609-3fcfd67a4950"},
},
want: []uuid.UUID{uuid.MustParse("a8398a0b-876d-48ed-b609-3fcfd67a4950")},
},
{
name: "only name",
args: args{
tunnels: []*tunnelstore.Tunnel{
{
ID: uuid.MustParse("a8398a0b-876d-48ed-b609-3fcfd67a4950"),
Name: "tunnel1",
},
},
inputs: []string{"tunnel1"},
},
want: []uuid.UUID{uuid.MustParse("a8398a0b-876d-48ed-b609-3fcfd67a4950")},
},
{
name: "both UUID and name",
args: args{
tunnels: []*tunnelstore.Tunnel{
{
ID: uuid.MustParse("a8398a0b-876d-48ed-b609-3fcfd67a4950"),
Name: "tunnel1",
},
{
ID: uuid.MustParse("bf028b68-744f-466e-97f8-c46161d80aa5"),
Name: "tunnel2",
},
},
inputs: []string{"tunnel1", "bf028b68-744f-466e-97f8-c46161d80aa5"},
},
want: []uuid.UUID{
uuid.MustParse("a8398a0b-876d-48ed-b609-3fcfd67a4950"),
uuid.MustParse("bf028b68-744f-466e-97f8-c46161d80aa5"),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := findIDs(tt.args.tunnels, tt.args.inputs)
if (err != nil) != tt.wantErr {
t.Errorf("findIDs() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("findIDs() = %v, want %v", got, tt.want)
}
})
}
}
type mockFileSystem struct {
rf func(string) ([]byte, error)
vfp func(string) bool
}
func (fs mockFileSystem) validFilePath(path string) bool {
return fs.vfp(path)
}
func (fs mockFileSystem) readFile(filePath string) ([]byte, error) {
return fs.rf(filePath)
}
func Test_subcommandContext_findCredentials(t *testing.T) {
type fields struct {
c *cli.Context
log *zerolog.Logger
isUIEnabled bool
fs fileSystem
tunnelstoreClient tunnelstore.Client
userCredential *userCredential
}
type args struct {
tunnelID uuid.UUID
}
oldCertPath := "old_cert.json"
newCertPath := "new_cert.json"
accountTag := "0000d4d14e84bd4ae5a6a02e0000ac63"
secret := []byte{211, 79, 177, 245, 179, 194, 152, 127, 140, 71, 18, 46, 183, 209, 10, 24, 192, 150, 55, 249, 211, 16, 167, 30, 113, 51, 152, 168, 72, 100, 205, 144}
secretB64 := base64.StdEncoding.EncodeToString(secret)
tunnelID := uuid.MustParse("df5ed608-b8b4-4109-89f3-9f2cf199df64")
name := "mytunnel"
fs := mockFileSystem{
rf: func(filePath string) ([]byte, error) {
if filePath == oldCertPath {
// An old credentials file created before TUN-3581 added the new fields
return []byte(fmt.Sprintf(`{"AccountTag":"%s","TunnelSecret":"%s"}`, accountTag, secretB64)), nil
}
if filePath == newCertPath {
// A new credentials file created after TUN-3581 with its new fields.
return []byte(fmt.Sprintf(`{"AccountTag":"%s","TunnelSecret":"%s","TunnelID":"%s","TunnelName":"%s"}`, accountTag, secretB64, tunnelID, name)), nil
}
return nil, errors.New("file not found")
},
vfp: func(string) bool { return true },
}
log := zerolog.Nop()
tests := []struct {
name string
fields fields
args args
want connection.Credentials
wantErr bool
}{
{
name: "Filepath given leads to old credentials file",
fields: fields{
log: &log,
fs: fs,
c: func() *cli.Context {
flagSet := flag.NewFlagSet("test0", flag.PanicOnError)
flagSet.String(CredFileFlag, oldCertPath, "")
c := cli.NewContext(cli.NewApp(), flagSet, nil)
_ = c.Set(CredFileFlag, oldCertPath)
return c
}(),
},
args: args{
tunnelID: tunnelID,
},
want: connection.Credentials{
AccountTag: accountTag,
TunnelID: tunnelID,
TunnelSecret: secret,
},
},
{
name: "Filepath given leads to new credentials file",
fields: fields{
log: &log,
fs: fs,
c: func() *cli.Context {
flagSet := flag.NewFlagSet("test0", flag.PanicOnError)
flagSet.String(CredFileFlag, newCertPath, "")
c := cli.NewContext(cli.NewApp(), flagSet, nil)
_ = c.Set(CredFileFlag, newCertPath)
return c
}(),
},
args: args{
tunnelID: tunnelID,
},
want: connection.Credentials{
AccountTag: accountTag,
TunnelID: tunnelID,
TunnelSecret: secret,
TunnelName: name,
},
},
{
name: "TUNNEL_CRED_CONTENTS given contains old credentials contents",
fields: fields{
log: &log,
fs: fs,
c: func() *cli.Context {
flagSet := flag.NewFlagSet("test0", flag.PanicOnError)
flagSet.String(CredContentsFlag, "", "")
c := cli.NewContext(cli.NewApp(), flagSet, nil)
_ = c.Set(CredContentsFlag, fmt.Sprintf(`{"AccountTag":"%s","TunnelSecret":"%s"}`, accountTag, secretB64))
return c
}(),
},
args: args{
tunnelID: tunnelID,
},
want: connection.Credentials{
AccountTag: accountTag,
TunnelID: tunnelID,
TunnelSecret: secret,
},
},
{
name: "TUNNEL_CRED_CONTENTS given contains new credentials contents",
fields: fields{
log: &log,
fs: fs,
c: func() *cli.Context {
flagSet := flag.NewFlagSet("test0", flag.PanicOnError)
flagSet.String(CredContentsFlag, "", "")
c := cli.NewContext(cli.NewApp(), flagSet, nil)
_ = c.Set(CredContentsFlag, fmt.Sprintf(`{"AccountTag":"%s","TunnelSecret":"%s","TunnelID":"%s","TunnelName":"%s"}`, accountTag, secretB64, tunnelID, name))
return c
}(),
},
args: args{
tunnelID: tunnelID,
},
want: connection.Credentials{
AccountTag: accountTag,
TunnelID: tunnelID,
TunnelSecret: secret,
TunnelName: name,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sc := &subcommandContext{
c: tt.fields.c,
log: tt.fields.log,
isUIEnabled: tt.fields.isUIEnabled,
fs: tt.fields.fs,
tunnelstoreClient: tt.fields.tunnelstoreClient,
userCredential: tt.fields.userCredential,
}
got, err := sc.findCredentials(tt.args.tunnelID)
if (err != nil) != tt.wantErr {
t.Errorf("subcommandContext.findCredentials() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("subcommandContext.findCredentials() = %v, want %v", got, tt.want)
}
})
}
}
type deleteMockTunnelStore struct {
tunnelstore.Client
mockTunnels map[uuid.UUID]mockTunnelBehaviour
deletedTunnelIDs []uuid.UUID
}
type mockTunnelBehaviour struct {
tunnel tunnelstore.Tunnel
deleteErr error
cleanupErr error
}
func newDeleteMockTunnelStore(tunnels ...mockTunnelBehaviour) *deleteMockTunnelStore {
mockTunnels := make(map[uuid.UUID]mockTunnelBehaviour)
for _, tunnel := range tunnels {
mockTunnels[tunnel.tunnel.ID] = tunnel
}
return &deleteMockTunnelStore{
mockTunnels: mockTunnels,
deletedTunnelIDs: make([]uuid.UUID, 0),
}
}
func (d *deleteMockTunnelStore) GetTunnel(tunnelID uuid.UUID) (*tunnelstore.Tunnel, error) {
tunnel, ok := d.mockTunnels[tunnelID]
if !ok {
return nil, fmt.Errorf("Couldn't find tunnel: %v", tunnelID)
}
return &tunnel.tunnel, nil
}
func (d *deleteMockTunnelStore) DeleteTunnel(tunnelID uuid.UUID) error {
tunnel, ok := d.mockTunnels[tunnelID]
if !ok {
return fmt.Errorf("Couldn't find tunnel: %v", tunnelID)
}
if tunnel.deleteErr != nil {
return tunnel.deleteErr
}
d.deletedTunnelIDs = append(d.deletedTunnelIDs, tunnelID)
tunnel.tunnel.DeletedAt = time.Now()
delete(d.mockTunnels, tunnelID)
return nil
}
func (d *deleteMockTunnelStore) CleanupConnections(tunnelID uuid.UUID, _ *tunnelstore.CleanupParams) error {
tunnel, ok := d.mockTunnels[tunnelID]
if !ok {
return fmt.Errorf("Couldn't find tunnel: %v", tunnelID)
}
return tunnel.cleanupErr
}
func Test_subcommandContext_Delete(t *testing.T) {
type fields struct {
c *cli.Context
log *zerolog.Logger
isUIEnabled bool
fs fileSystem
tunnelstoreClient *deleteMockTunnelStore
userCredential *userCredential
}
type args struct {
tunnelIDs []uuid.UUID
}
newCertPath := "new_cert.json"
tunnelID1 := uuid.MustParse("df5ed608-b8b4-4109-89f3-9f2cf199df64")
tunnelID2 := uuid.MustParse("af5ed608-b8b4-4109-89f3-9f2cf199df64")
log := zerolog.Nop()
var tests = []struct {
name string
fields fields
args args
want []uuid.UUID
wantErr bool
}{
{
name: "clean up continues if credentials are not found",
fields: fields{
log: &log,
fs: mockFileSystem{
rf: func(filePath string) ([]byte, error) {
return nil, errors.New("file not found")
},
vfp: func(string) bool { return true },
},
c: func() *cli.Context {
flagSet := flag.NewFlagSet("test0", flag.PanicOnError)
flagSet.String(CredFileFlag, newCertPath, "")
c := cli.NewContext(cli.NewApp(), flagSet, nil)
_ = c.Set(CredFileFlag, newCertPath)
return c
}(),
tunnelstoreClient: newDeleteMockTunnelStore(
mockTunnelBehaviour{
tunnel: tunnelstore.Tunnel{ID: tunnelID1},
},
mockTunnelBehaviour{
tunnel: tunnelstore.Tunnel{ID: tunnelID2},
},
),
},
args: args{
tunnelIDs: []uuid.UUID{tunnelID1, tunnelID2},
},
want: []uuid.UUID{tunnelID1, tunnelID2},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sc := &subcommandContext{
c: tt.fields.c,
log: tt.fields.log,
isUIEnabled: tt.fields.isUIEnabled,
fs: tt.fields.fs,
tunnelstoreClient: tt.fields.tunnelstoreClient,
userCredential: tt.fields.userCredential,
}
err := sc.delete(tt.args.tunnelIDs)
if (err != nil) != tt.wantErr {
t.Errorf("subcommandContext.findCredentials() error = %v, wantErr %v", err, tt.wantErr)
return
}
got := tt.fields.tunnelstoreClient.deletedTunnelIDs
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("subcommandContext.findCredentials() = %v, want %v", got, tt.want)
return
}
})
}
}