summaryrefslogtreecommitdiff
path: root/internal/clients/connectors/serverless.go
blob: 631186aebe583d724e7b3b72c7db1aa940b9f05c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package connectors

import (
	"context"
	"io"

	"github.com/mimecast/dtail/internal/clients/handlers"
	"github.com/mimecast/dtail/internal/config"
	"github.com/mimecast/dtail/internal/io/dlog"
	serverHandlers "github.com/mimecast/dtail/internal/server/handlers"
	user "github.com/mimecast/dtail/internal/user/server"
)

// Serverless creates a server object directly without TCP.
type Serverless struct {
	handler  handlers.Handler
	commands []string
	userName string
}

// NewServerless starts a new serverless session.
func NewServerless(userName string, handler handlers.Handler,
	commands []string) *Serverless {

	dlog.Client.Debug("Creating new serverless connector", handler, commands)
	return &Serverless{
		userName: userName,
		handler:  handler,
		commands: commands,
	}
}

// Server returns serverless server indicator.
func (s *Serverless) Server() string {
	return "local(serverless)"
}

// Handler returns the handler used for the serverless connection.
func (s *Serverless) Handler() handlers.Handler {
	return s.handler
}

// Start the serverless connection.
func (s *Serverless) Start(ctx context.Context, cancel context.CancelFunc,
	throttleCh, statsCh chan struct{}) {

	dlog.Client.Debug("Starting serverless connector")
	go func() {
		defer cancel()
		if err := s.handle(ctx, cancel); err != nil {
			dlog.Client.Warn(err)
		}
	}()
	<-ctx.Done()
}

func (s *Serverless) handle(ctx context.Context, cancel context.CancelFunc) error {
	dlog.Client.Debug("Creating server handler for a serverless session")

	user, err := user.New(s.userName, s.Server())
	if err != nil {
		return err
	}

	var serverHandler serverHandlers.Handler
	switch s.userName {
	case config.HealthUser:
		dlog.Client.Debug("Creating serverless health handler")
		serverHandler = serverHandlers.NewHealthHandler(user)
	default:
		dlog.Client.Debug("Creating serverless server handler")
		serverHandler = serverHandlers.NewServerHandler(
			user,
			make(chan struct{}, config.Server.MaxConcurrentCats),
			make(chan struct{}, config.Server.MaxConcurrentTails),
		)
	}

	terminate := func() {
		dlog.Client.Debug("Terminating serverless connection")
		serverHandler.Shutdown()
		cancel()
	}

	go func() {
		defer terminate()
		if _, err := io.Copy(serverHandler, s.handler); err != nil {
			dlog.Client.Trace(err)
		}
		dlog.Client.Trace("io.Copy(serverHandler, s.handler) => done")
	}()
	go func() {
		defer terminate()
		if _, err := io.Copy(s.handler, serverHandler); err != nil {
			dlog.Client.Trace(err)
		}
		dlog.Client.Trace("io.Copy(s.handler, serverHandler) => done")
	}()
	go func() {
		defer terminate()
		select {
		case <-s.handler.Done():
			dlog.Client.Trace("<-s.handler.Done()")
		case <-ctx.Done():
			dlog.Client.Trace("<-ctx.Done()")
		}
	}()

	// Send all commands to client.
	for _, command := range s.commands {
		dlog.Client.Debug("Sending command to serverless server", command)
		if err := s.handler.SendMessage(command); err != nil {
			dlog.Client.Debug(err)
		}
	}

	<-ctx.Done()
	dlog.Client.Trace("s.handler.Shutdown()")
	s.handler.Shutdown()
	return nil
}