Add message queue service factory (#1537)
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
Copyright 2020 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package factory
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
|
||||
fv1 "github.com/fission/fission/pkg/apis/core/v1"
|
||||
"github.com/fission/fission/pkg/mqtrigger/messageQueue"
|
||||
)
|
||||
|
||||
var (
|
||||
messageQueueFactories = make(map[fv1.MessageQueueType]MessageQueueFactory)
|
||||
lock = sync.Mutex{}
|
||||
)
|
||||
|
||||
type (
|
||||
MessageQueueFactory interface {
|
||||
Create(logger *zap.Logger, config messageQueue.Config, routerURL string) (messageQueue.MessageQueue, error)
|
||||
}
|
||||
)
|
||||
|
||||
func Register(mqType fv1.MessageQueueType, factory MessageQueueFactory) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
if factory == nil {
|
||||
panic("Nil message queue factory")
|
||||
}
|
||||
|
||||
_, registered := messageQueueFactories[mqType]
|
||||
if registered {
|
||||
panic("Message queue factory already register")
|
||||
}
|
||||
|
||||
messageQueueFactories[mqType] = factory
|
||||
}
|
||||
|
||||
func Create(logger *zap.Logger, mqType fv1.MessageQueueType, mqConfig messageQueue.Config, routerUrl string) (messageQueue.MessageQueue, error) {
|
||||
factory, registered := messageQueueFactories[mqType]
|
||||
if !registered {
|
||||
return nil, errors.Errorf("no supported message queue type found for %q", mqType)
|
||||
}
|
||||
return factory.Create(logger, mqConfig, routerUrl)
|
||||
}
|
||||
@@ -30,14 +30,21 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/storage"
|
||||
"github.com/fission/fission/pkg/utils"
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
|
||||
fv1 "github.com/fission/fission/pkg/apis/core/v1"
|
||||
"github.com/fission/fission/pkg/mqtrigger/factory"
|
||||
"github.com/fission/fission/pkg/mqtrigger/messageQueue"
|
||||
"github.com/fission/fission/pkg/mqtrigger/validator"
|
||||
"github.com/fission/fission/pkg/utils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
factory.Register(fv1.MessageQueueTypeASQ, &Factory{})
|
||||
validator.Register(fv1.MessageQueueTypeASQ, IsTopicValid)
|
||||
}
|
||||
|
||||
// TODO: some of these constants should probably be environment variables
|
||||
const (
|
||||
// AzureQueuePollingInterval is the polling interval (default is 1 minute).
|
||||
@@ -105,6 +112,12 @@ type AzureHTTPClient interface {
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
type Factory struct{}
|
||||
|
||||
func (factory *Factory) Create(logger *zap.Logger, mqCfg messageQueue.Config, routerUrl string) (messageQueue.MessageQueue, error) {
|
||||
return New(logger, mqCfg, routerUrl)
|
||||
}
|
||||
|
||||
type azureQueueService struct {
|
||||
service storage.QueueServiceClient
|
||||
}
|
||||
@@ -179,7 +192,7 @@ func newAzureQueueService(client storage.Client) AzureQueueService {
|
||||
}
|
||||
}
|
||||
|
||||
func New(logger *zap.Logger, routerURL string, config messageQueue.Config) (messageQueue.MessageQueue, error) {
|
||||
func New(logger *zap.Logger, mqCfg messageQueue.Config, routerUrl string) (messageQueue.MessageQueue, error) {
|
||||
account := os.Getenv("AZURE_STORAGE_ACCOUNT_NAME")
|
||||
if len(account) == 0 {
|
||||
return nil, errors.New("Required environment variable 'AZURE_STORAGE_ACCOUNT_NAME' is not set")
|
||||
@@ -198,7 +211,7 @@ func New(logger *zap.Logger, routerURL string, config messageQueue.Config) (mess
|
||||
}
|
||||
return &AzureStorageConnection{
|
||||
logger: logger.Named("azue_storage"),
|
||||
routerURL: routerURL,
|
||||
routerURL: routerUrl,
|
||||
service: newAzureQueueService(client),
|
||||
httpClient: &http.Client{
|
||||
Timeout: AzureFunctionInvocationTimeout,
|
||||
|
||||
@@ -113,10 +113,10 @@ func TestNewStorageConnectionMissingAccountName(t *testing.T) {
|
||||
logger, err := zap.NewDevelopment()
|
||||
panicIf(err)
|
||||
|
||||
connection, err := New(logger, DummyRouterURL, messageQueue.Config{
|
||||
connection, err := New(logger, messageQueue.Config{
|
||||
MQType: fv1.MessageQueueTypeASQ,
|
||||
Url: "",
|
||||
})
|
||||
}, DummyRouterURL)
|
||||
require.Nil(t, connection)
|
||||
require.Error(t, err, "Required environment variable 'AZURE_STORAGE_ACCOUNT_NAME' is not set")
|
||||
}
|
||||
@@ -126,10 +126,10 @@ func TestNewStorageConnectionMissingAccessKey(t *testing.T) {
|
||||
panicIf(err)
|
||||
|
||||
_ = os.Setenv("AZURE_STORAGE_ACCOUNT_NAME", "accountname")
|
||||
connection, err := New(logger, DummyRouterURL, messageQueue.Config{
|
||||
connection, err := New(logger, messageQueue.Config{
|
||||
MQType: fv1.MessageQueueTypeASQ,
|
||||
Url: "",
|
||||
})
|
||||
}, DummyRouterURL)
|
||||
_ = os.Unsetenv("AZURE_STORAGE_ACCOUNT_NAME")
|
||||
require.Nil(t, connection)
|
||||
require.Error(t, err, "Required environment variable 'AZURE_STORAGE_ACCOUNT_KEY' is not set")
|
||||
@@ -141,10 +141,10 @@ func TestNewStorageConnection(t *testing.T) {
|
||||
|
||||
_ = os.Setenv("AZURE_STORAGE_ACCOUNT_NAME", "accountname")
|
||||
_ = os.Setenv("AZURE_STORAGE_ACCOUNT_KEY", "bm90IGEga2V5")
|
||||
connection, err := New(logger, DummyRouterURL, messageQueue.Config{
|
||||
connection, err := New(logger, messageQueue.Config{
|
||||
MQType: "azure-storage-queue",
|
||||
Url: "",
|
||||
})
|
||||
}, DummyRouterURL)
|
||||
_ = os.Unsetenv("AZURE_STORAGE_ACCOUNT_NAME")
|
||||
_ = os.Unsetenv("AZURE_STORAGE_ACCOUNT_KEY")
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -33,10 +33,17 @@ import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
fv1 "github.com/fission/fission/pkg/apis/core/v1"
|
||||
"github.com/fission/fission/pkg/mqtrigger/factory"
|
||||
"github.com/fission/fission/pkg/mqtrigger/messageQueue"
|
||||
"github.com/fission/fission/pkg/mqtrigger/validator"
|
||||
"github.com/fission/fission/pkg/utils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
factory.Register(fv1.MessageQueueTypeKafka, &Factory{})
|
||||
validator.Register(fv1.MessageQueueTypeKafka, IsTopicValid)
|
||||
}
|
||||
|
||||
var (
|
||||
// Need to use raw string to support escape sequence for - & . chars
|
||||
validKafkaTopicName = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9\-\._]*[a-zA-Z0-9]$`)
|
||||
@@ -51,9 +58,15 @@ type (
|
||||
authKeys map[string][]byte
|
||||
tls bool
|
||||
}
|
||||
|
||||
Factory struct{}
|
||||
)
|
||||
|
||||
func New(logger *zap.Logger, routerUrl string, mqCfg messageQueue.Config) (messageQueue.MessageQueue, error) {
|
||||
func (factory *Factory) Create(logger *zap.Logger, mqCfg messageQueue.Config, routerUrl string) (messageQueue.MessageQueue, error) {
|
||||
return New(logger, mqCfg, routerUrl)
|
||||
}
|
||||
|
||||
func New(logger *zap.Logger, mqCfg messageQueue.Config, routerUrl string) (messageQueue.MessageQueue, error) {
|
||||
if len(routerUrl) == 0 || len(mqCfg.Url) == 0 {
|
||||
return nil, errors.New("the router URL or MQ URL is empty")
|
||||
}
|
||||
@@ -340,7 +353,8 @@ func errorHandler(logger *zap.Logger, trigger *fv1.MessageQueueTrigger, producer
|
||||
}
|
||||
}
|
||||
|
||||
// The validation is based on Kafka's internal implementation: https://github.com/apache/kafka/blob/cde6d18983b5d58199f8857d8d61d7efcbe6e54a/clients/src/main/java/org/apache/kafka/common/internals/Topic.java#L36-L47
|
||||
// The validation is based on Kafka's internal implementation:
|
||||
// https://github.com/apache/kafka/blob/cde6d18983b5d58199f8857d8d61d7efcbe6e54a/clients/src/main/java/org/apache/kafka/common/internals/Topic.java#L36-L47
|
||||
func IsTopicValid(topic string) bool {
|
||||
if len(topic) == 0 {
|
||||
return false
|
||||
|
||||
@@ -28,10 +28,17 @@ import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
fv1 "github.com/fission/fission/pkg/apis/core/v1"
|
||||
"github.com/fission/fission/pkg/mqtrigger/factory"
|
||||
"github.com/fission/fission/pkg/mqtrigger/messageQueue"
|
||||
"github.com/fission/fission/pkg/mqtrigger/validator"
|
||||
"github.com/fission/fission/pkg/utils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
factory.Register(fv1.MessageQueueTypeNats, &Factory{})
|
||||
validator.Register(fv1.MessageQueueTypeNats, IsTopicValid)
|
||||
}
|
||||
|
||||
const (
|
||||
natsClusterID = "fissionMQTrigger"
|
||||
natsProtocol = "nats://"
|
||||
@@ -45,9 +52,15 @@ type (
|
||||
nsConn ns.Conn
|
||||
routerUrl string
|
||||
}
|
||||
|
||||
Factory struct{}
|
||||
)
|
||||
|
||||
func New(logger *zap.Logger, routerUrl string, mqCfg messageQueue.Config) (messageQueue.MessageQueue, error) {
|
||||
func (factory *Factory) Create(logger *zap.Logger, mqCfg messageQueue.Config, routerUrl string) (messageQueue.MessageQueue, error) {
|
||||
return New(logger, mqCfg, routerUrl)
|
||||
}
|
||||
|
||||
func New(logger *zap.Logger, mqCfg messageQueue.Config, routerUrl string) (messageQueue.MessageQueue, error) {
|
||||
conn, err := ns.Connect(natsClusterID, natsClientID, ns.NatsURL(mqCfg.Url),
|
||||
ns.SetConnectionLostHandler(func(conn ns.Conn, reason error) {
|
||||
// TODO: Better way to handle connection lost problem.
|
||||
|
||||
+16
-27
@@ -26,9 +26,6 @@ import (
|
||||
fv1 "github.com/fission/fission/pkg/apis/core/v1"
|
||||
"github.com/fission/fission/pkg/crd"
|
||||
"github.com/fission/fission/pkg/mqtrigger/messageQueue"
|
||||
"github.com/fission/fission/pkg/mqtrigger/messageQueue/azurequeuestorage"
|
||||
"github.com/fission/fission/pkg/mqtrigger/messageQueue/kafka"
|
||||
"github.com/fission/fission/pkg/mqtrigger/messageQueue/nats"
|
||||
"github.com/fission/fission/pkg/utils"
|
||||
)
|
||||
|
||||
@@ -42,11 +39,12 @@ type (
|
||||
requestType int
|
||||
|
||||
MessageQueueTriggerManager struct {
|
||||
logger *zap.Logger
|
||||
reqChan chan request
|
||||
triggers map[string]*triggerSubscription
|
||||
fissionClient *crd.FissionClient
|
||||
messageQueue messageQueue.MessageQueue
|
||||
logger *zap.Logger
|
||||
reqChan chan request
|
||||
triggers map[string]*triggerSubscription
|
||||
fissionClient *crd.FissionClient
|
||||
messageQueueType fv1.MessageQueueType
|
||||
messageQueue messageQueue.MessageQueue
|
||||
}
|
||||
|
||||
triggerSubscription struct {
|
||||
@@ -66,13 +64,14 @@ type (
|
||||
)
|
||||
|
||||
func MakeMessageQueueTriggerManager(logger *zap.Logger,
|
||||
fissionClient *crd.FissionClient, messageQueue messageQueue.MessageQueue) *MessageQueueTriggerManager {
|
||||
fissionClient *crd.FissionClient, mqType fv1.MessageQueueType, messageQueue messageQueue.MessageQueue) *MessageQueueTriggerManager {
|
||||
mqTriggerMgr := MessageQueueTriggerManager{
|
||||
logger: logger.Named("message_queue_trigger_manager"),
|
||||
reqChan: make(chan request),
|
||||
triggers: make(map[string]*triggerSubscription),
|
||||
fissionClient: fissionClient,
|
||||
messageQueue: messageQueue,
|
||||
logger: logger.Named("message_queue_trigger_manager"),
|
||||
reqChan: make(chan request),
|
||||
triggers: make(map[string]*triggerSubscription),
|
||||
fissionClient: fissionClient,
|
||||
messageQueueType: mqType,
|
||||
messageQueue: messageQueue,
|
||||
}
|
||||
return &mqTriggerMgr
|
||||
}
|
||||
@@ -154,7 +153,9 @@ func (mqt *MessageQueueTriggerManager) syncTriggers() {
|
||||
newTriggerMap := make(map[string]*fv1.MessageQueueTrigger)
|
||||
for index := range newTriggers.Items {
|
||||
newTrigger := &newTriggers.Items[index]
|
||||
newTriggerMap[crd.CacheKey(&newTrigger.ObjectMeta)] = newTrigger
|
||||
if newTrigger.Spec.MessageQueueType == mqt.messageQueueType {
|
||||
newTriggerMap[crd.CacheKey(&newTrigger.ObjectMeta)] = newTrigger
|
||||
}
|
||||
}
|
||||
|
||||
// get current set of triggers
|
||||
@@ -205,15 +206,3 @@ func (mqt *MessageQueueTriggerManager) syncTriggers() {
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func IsTopicValid(mqType fv1.MessageQueueType, topic string) bool {
|
||||
switch mqType {
|
||||
case fv1.MessageQueueTypeNats:
|
||||
return nats.IsTopicValid(topic)
|
||||
case fv1.MessageQueueTypeASQ:
|
||||
return azurequeuestorage.IsTopicValid(topic)
|
||||
case fv1.MessageQueueTypeKafka:
|
||||
return kafka.IsTopicValid(topic)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright 2020 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package validator
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
topicValidators = make(map[string]TopicValidator)
|
||||
lock = sync.Mutex{}
|
||||
)
|
||||
|
||||
type (
|
||||
TopicValidator func(topic string) bool
|
||||
)
|
||||
|
||||
func Register(mqType string, validator TopicValidator) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
if validator == nil {
|
||||
panic("Nil message queue topic validator")
|
||||
}
|
||||
|
||||
_, registered := topicValidators[mqType]
|
||||
if registered {
|
||||
panic("Message queue topic validator already register")
|
||||
}
|
||||
|
||||
topicValidators[mqType] = validator
|
||||
}
|
||||
|
||||
func IsValidTopic(mqType string, topic string) bool {
|
||||
validator, registered := topicValidators[mqType]
|
||||
if !registered {
|
||||
return false
|
||||
}
|
||||
return validator(topic)
|
||||
}
|
||||
|
||||
func IsValidMessageQueue(mqType string) bool {
|
||||
_, registered := topicValidators[mqType]
|
||||
return registered
|
||||
}
|
||||
Reference in New Issue
Block a user