This repository has been archived by the owner on Nov 16, 2020. It is now read-only.
/
manager.go
211 lines (174 loc) · 6 KB
/
manager.go
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
///////////////////////////////////////////////////////////////////////
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
///////////////////////////////////////////////////////////////////////
package subscriptions
import (
"context"
"fmt"
"sync"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/vmware/dispatch/pkg/api/v1"
"github.com/vmware/dispatch/pkg/entity-store"
"github.com/vmware/dispatch/pkg/client"
"github.com/vmware/dispatch/pkg/event-manager/helpers"
"github.com/vmware/dispatch/pkg/event-manager/subscriptions/entities"
"github.com/vmware/dispatch/pkg/events"
"github.com/vmware/dispatch/pkg/trace"
)
// Manager defines the subscription manager interface
type Manager interface {
Run(context.Context, []*entities.Subscription) error
Create(context.Context, *entities.Subscription) error
Update(context.Context, *entities.Subscription) error
Delete(context.Context, *entities.Subscription) error
}
type defaultManager struct {
queue events.Transport
fnClient client.FunctionsClient
sync.RWMutex
activeSubs map[string]events.Subscription
}
// NewManager creates a new subscription manager
func NewManager(mq events.Transport, fnClient client.FunctionsClient) (Manager, error) {
ec := defaultManager{
queue: mq,
fnClient: fnClient,
activeSubs: make(map[string]events.Subscription),
}
return &ec, nil
}
func (m *defaultManager) Run(ctx context.Context, subscriptions []*entities.Subscription) error {
span, ctx := trace.Trace(ctx, "")
defer span.Finish()
log.Debugf("event consumer initializing")
for _, sub := range subscriptions {
log.Debugf("Processing sub %s", sub.Name)
m.Create(ctx, sub)
}
return nil
}
// Create creates an active subscription to Message Queue. Active subscription connects
// to Message Queue and executes a handler for every event received.
func (m *defaultManager) Create(ctx context.Context, sub *entities.Subscription) error {
span, ctx := trace.Trace(ctx, "")
defer span.Finish()
span.SetTag("eventType", sub.EventType)
span.SetTag("functionName", sub.Function)
m.Lock()
defer m.Unlock()
if eventSub, ok := m.activeSubs[sub.ID]; ok {
log.Debugf("types.Subscription for %s/%s already existed, unsubscribing", sub.EventType, sub.Function)
eventSub.Unsubscribe()
delete(m.activeSubs, sub.ID)
}
eventSub, err := m.createSubscription(ctx, sub)
if err != nil {
span.LogKV("error", err)
return err
}
m.activeSubs[sub.ID] = eventSub
return nil
}
// Update updates a subscription
func (m *defaultManager) Update(ctx context.Context, sub *entities.Subscription) error {
span, ctx := trace.Trace(ctx, "")
defer span.Finish()
span.SetTag("eventType", sub.EventType)
span.SetTag("functionName", sub.Function)
m.Lock()
defer m.Unlock()
eventSub, ok := m.activeSubs[sub.ID]
if ok && sub.Status == entitystore.StatusREADY {
// subscription is active as expected, do nothing
return nil
}
if ok {
eventSub.Unsubscribe()
delete(m.activeSubs, sub.ID)
}
eventSub, err := m.createSubscription(ctx, sub)
if err != nil {
span.LogKV("error", err)
return err
}
m.activeSubs[sub.ID] = eventSub
log.Infof("subscription %s for event type %s has been updated", sub.Name, sub.EventType)
return nil
}
func (m *defaultManager) createSubscription(ctx context.Context, sub *entities.Subscription) (events.Subscription, error) {
topic := sub.EventType
// subscribe
eventSub, err := m.queue.Subscribe(ctx, topic, sub.OrganizationID, m.handler(ctx, sub))
if err != nil {
err = errors.Wrapf(err, "unable to create a subscription for event %s and function %s", sub.EventType, sub.Function)
log.Error(err)
return nil, err
}
return eventSub, nil
}
// Delete deletes a subscription from pool of active subscriptions.
func (m *defaultManager) Delete(ctx context.Context, sub *entities.Subscription) error {
span, ctx := trace.Trace(ctx, "")
defer span.Finish()
span.SetTag("eventType", sub.EventType)
span.SetTag("functionName", sub.Function)
m.Lock()
defer m.Unlock()
if eventSub, ok := m.activeSubs[sub.ID]; ok {
eventSub.Unsubscribe()
delete(m.activeSubs, sub.ID)
}
log.Debugf("Deleting subscription topic=%s id=%s revision=%d", sub.EventType, sub.Name, sub.Revision)
return nil
}
// Shutdown ends event controller loop
func (m *defaultManager) Shutdown() {
log.Infof("Event controller shutdown")
m.Lock()
defer m.Unlock()
for _, sub := range m.activeSubs {
sub.Unsubscribe()
}
}
// handler creates a function to handle the incoming event. it takes name of the function to be invoked as an argument.
func (m *defaultManager) handler(ctx context.Context, sub *entities.Subscription) func(context.Context, *events.CloudEvent) {
span, _ := trace.Trace(ctx, "")
defer span.Finish()
span.SetTag("eventType", sub.EventType)
span.SetTag("functionName", sub.Function)
return func(ctx context.Context, event *events.CloudEvent) {
span, ctx := trace.Trace(ctx, "EventHandler")
defer span.Finish()
span.SetTag("eventType", sub.EventType)
span.SetTag("functionName", sub.Function)
m.runFunction(ctx, sub.OrganizationID, sub.Function, event, sub.Secrets)
}
}
// executes a function by connecting to function manager
func (m *defaultManager) runFunction(ctx context.Context, organizationID string, fnName string, event *events.CloudEvent, secrets []string) {
span, ctx := trace.Trace(ctx, "")
defer span.Finish()
span.SetTag("eventType", event.EventType)
span.SetTag("functionName", fnName)
run := v1.Run{
Blocking: false,
FunctionName: fnName,
Input: event.Data,
}
eventCopy := *event
eventCopy.Data = nil
run.Event = helpers.CloudEventToAPI(&eventCopy)
result, err := m.fnClient.RunFunction(ctx, organizationID, &run)
if err != nil {
errorMsg := fmt.Sprintf("Unable to run function %s, error from function manager: %+v", fnName, err)
span.LogKV("error", errorMsg)
log.Error(errorMsg)
return
}
span.LogKV("functionName", result.FunctionName,
"functionResult", result.Output)
log.Debugf("Function %s returned %+v", result.FunctionName, result.Output)
return
}