|
- package machinery
-
- import (
- "errors"
- "fmt"
- neturl "net/url"
- "os"
- "strconv"
- "strings"
-
- "github.com/RichardKnop/machinery/v1/config"
-
- amqpbroker "github.com/RichardKnop/machinery/v1/brokers/amqp"
- eagerbroker "github.com/RichardKnop/machinery/v1/brokers/eager"
- gcppubsubbroker "github.com/RichardKnop/machinery/v1/brokers/gcppubsub"
- brokeriface "github.com/RichardKnop/machinery/v1/brokers/iface"
- redisbroker "github.com/RichardKnop/machinery/v1/brokers/redis"
- sqsbroker "github.com/RichardKnop/machinery/v1/brokers/sqs"
-
- amqpbackend "github.com/RichardKnop/machinery/v1/backends/amqp"
- dynamobackend "github.com/RichardKnop/machinery/v1/backends/dynamodb"
- eagerbackend "github.com/RichardKnop/machinery/v1/backends/eager"
- backendiface "github.com/RichardKnop/machinery/v1/backends/iface"
- memcachebackend "github.com/RichardKnop/machinery/v1/backends/memcache"
- mongobackend "github.com/RichardKnop/machinery/v1/backends/mongo"
- nullbackend "github.com/RichardKnop/machinery/v1/backends/null"
- redisbackend "github.com/RichardKnop/machinery/v1/backends/redis"
- )
-
- // BrokerFactory creates a new object of iface.Broker
- // Currently only AMQP/S broker is supported
- func BrokerFactory(cnf *config.Config) (brokeriface.Broker, error) {
- if strings.HasPrefix(cnf.Broker, "amqp://") {
- return amqpbroker.New(cnf), nil
- }
-
- if strings.HasPrefix(cnf.Broker, "amqps://") {
- return amqpbroker.New(cnf), nil
- }
-
- if strings.HasPrefix(cnf.Broker, "redis://") {
- parts := strings.Split(cnf.Broker, "redis://")
- if len(parts) != 2 {
- return nil, fmt.Errorf(
- "Redis broker connection string should be in format redis://host:port, instead got %s",
- cnf.Broker,
- )
- }
-
- redisHost, redisPassword, redisDB, err := ParseRedisURL(cnf.Broker)
- if err != nil {
- return nil, err
- }
- return redisbroker.New(cnf, redisHost, redisPassword, "", redisDB), nil
- }
-
- if strings.HasPrefix(cnf.Broker, "redis+socket://") {
- redisSocket, redisPassword, redisDB, err := ParseRedisSocketURL(cnf.Broker)
- if err != nil {
- return nil, err
- }
-
- return redisbroker.New(cnf, "", redisPassword, redisSocket, redisDB), nil
- }
-
- if strings.HasPrefix(cnf.Broker, "eager") {
- return eagerbroker.New(), nil
- }
-
- if _, ok := os.LookupEnv("DISABLE_STRICT_SQS_CHECK"); ok {
- //disable SQS name check, so that users can use this with local simulated SQS
- //where sql broker url might not start with https://sqs
-
- //even when disabling strict SQS naming check, make sure its still a valid http URL
- if strings.HasPrefix(cnf.Broker, "https://") || strings.HasPrefix(cnf.Broker, "http://") {
- return sqsbroker.New(cnf), nil
- }
- } else {
- if strings.HasPrefix(cnf.Broker, "https://sqs") {
- return sqsbroker.New(cnf), nil
- }
- }
-
- if strings.HasPrefix(cnf.Broker, "gcppubsub://") {
- projectID, subscriptionName, err := ParseGCPPubSubURL(cnf.Broker)
- if err != nil {
- return nil, err
- }
- return gcppubsubbroker.New(cnf, projectID, subscriptionName)
- }
-
- return nil, fmt.Errorf("Factory failed with broker URL: %v", cnf.Broker)
- }
-
- // BackendFactory creates a new object of backends.Interface
- // Currently supported backends are AMQP/S and Memcache
- func BackendFactory(cnf *config.Config) (backendiface.Backend, error) {
- if strings.HasPrefix(cnf.ResultBackend, "amqp://") {
- return amqpbackend.New(cnf), nil
- }
-
- if strings.HasPrefix(cnf.ResultBackend, "amqps://") {
- return amqpbackend.New(cnf), nil
- }
-
- if strings.HasPrefix(cnf.ResultBackend, "memcache://") {
- parts := strings.Split(cnf.ResultBackend, "memcache://")
- if len(parts) != 2 {
- return nil, fmt.Errorf(
- "Memcache result backend connection string should be in format memcache://server1:port,server2:port, instead got %s",
- cnf.ResultBackend,
- )
- }
- servers := strings.Split(parts[1], ",")
- return memcachebackend.New(cnf, servers), nil
- }
-
- if strings.HasPrefix(cnf.ResultBackend, "redis://") {
- redisHost, redisPassword, redisDB, err := ParseRedisURL(cnf.ResultBackend)
- if err != nil {
- return nil, err
- }
-
- return redisbackend.New(cnf, redisHost, redisPassword, "", redisDB), nil
- }
-
- if strings.HasPrefix(cnf.ResultBackend, "redis+socket://") {
- redisSocket, redisPassword, redisDB, err := ParseRedisSocketURL(cnf.ResultBackend)
- if err != nil {
- return nil, err
- }
-
- return redisbackend.New(cnf, "", redisPassword, redisSocket, redisDB), nil
- }
-
- if strings.HasPrefix(cnf.ResultBackend, "mongodb://") ||
- strings.HasPrefix(cnf.ResultBackend, "mongodb+srv://") {
- return mongobackend.New(cnf)
- }
-
- if strings.HasPrefix(cnf.ResultBackend, "eager") {
- return eagerbackend.New(), nil
- }
-
- if strings.HasPrefix(cnf.ResultBackend, "null") {
- return nullbackend.New(), nil
- }
-
- if strings.HasPrefix(cnf.ResultBackend, "https://dynamodb") {
- return dynamobackend.New(cnf), nil
- }
-
- return nil, fmt.Errorf("Factory failed with result backend: %v", cnf.ResultBackend)
- }
-
- // ParseRedisURL ...
- func ParseRedisURL(url string) (host, password string, db int, err error) {
- // redis://pwd@host/db
-
- var u *neturl.URL
- u, err = neturl.Parse(url)
- if err != nil {
- return
- }
- if u.Scheme != "redis" {
- err = errors.New("No redis scheme found")
- return
- }
-
- if u.User != nil {
- var exists bool
- password, exists = u.User.Password()
- if !exists {
- password = u.User.Username()
- }
- }
-
- host = u.Host
-
- parts := strings.Split(u.Path, "/")
- if len(parts) == 1 {
- db = 0 //default redis db
- } else {
- db, err = strconv.Atoi(parts[1])
- if err != nil {
- db, err = 0, nil //ignore err here
- }
- }
-
- return
- }
-
- // ParseRedisSocketURL extracts Redis connection options from a URL with the
- // redis+socket:// scheme. This scheme is not standard (or even de facto) and
- // is used as a transitional mechanism until the the config package gains the
- // proper facilities to support socket-based connections.
- func ParseRedisSocketURL(url string) (path, password string, db int, err error) {
- parts := strings.Split(url, "redis+socket://")
- if parts[0] != "" {
- err = errors.New("No redis scheme found")
- return
- }
-
- // redis+socket://password@/path/to/file.soc:/db
-
- if len(parts) != 2 {
- err = fmt.Errorf("Redis socket connection string should be in format redis+socket://password@/path/to/file.sock:/db, instead got %s", url)
- return
- }
-
- remainder := parts[1]
-
- // Extract password if any
- parts = strings.SplitN(remainder, "@", 2)
- if len(parts) == 2 {
- password = parts[0]
- remainder = parts[1]
- } else {
- remainder = parts[0]
- }
-
- // Extract path
- parts = strings.SplitN(remainder, ":", 2)
- path = parts[0]
- if path == "" {
- err = fmt.Errorf("Redis socket connection string should be in format redis+socket://password@/path/to/file.sock:/db, instead got %s", url)
- return
- }
- if len(parts) == 2 {
- remainder = parts[1]
- }
-
- // Extract DB if any
- parts = strings.SplitN(remainder, "/", 2)
- if len(parts) == 2 {
- db, _ = strconv.Atoi(parts[1])
- }
-
- return
- }
-
- // ParseGCPPubSubURL Parse GCP Pub/Sub URL
- // url: gcppubsub://YOUR_GCP_PROJECT_ID/YOUR_PUBSUB_SUBSCRIPTION_NAME
- func ParseGCPPubSubURL(url string) (string, string, error) {
- parts := strings.Split(url, "gcppubsub://")
- if parts[0] != "" {
- return "", "", errors.New("No gcppubsub scheme found")
- }
-
- if len(parts) != 2 {
- return "", "", fmt.Errorf("gcppubsub scheme should be in format gcppubsub://YOUR_GCP_PROJECT_ID/YOUR_PUBSUB_SUBSCRIPTION_NAME, instead got %s", url)
- }
-
- remainder := parts[1]
-
- parts = strings.Split(remainder, "/")
- if len(parts) == 2 {
- if len(parts[0]) == 0 {
- return "", "", fmt.Errorf("gcppubsub scheme should be in format gcppubsub://YOUR_GCP_PROJECT_ID/YOUR_PUBSUB_SUBSCRIPTION_NAME, instead got %s", url)
- }
- if len(parts[1]) == 0 {
- return "", "", fmt.Errorf("gcppubsub scheme should be in format gcppubsub://YOUR_GCP_PROJECT_ID/YOUR_PUBSUB_SUBSCRIPTION_NAME, instead got %s", url)
- }
- return parts[0], parts[1], nil
- }
-
- return "", "", fmt.Errorf("gcppubsub scheme should be in format gcppubsub://YOUR_GCP_PROJECT_ID/YOUR_PUBSUB_SUBSCRIPTION_NAME, instead got %s", url)
- }
|