github.com/mweagle/Sparta@v1.15.0/decorator/discovery.go (about)

     1  package decorator
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"sync"
     7  
     8  	"github.com/aws/aws-sdk-go/aws"
     9  	"github.com/aws/aws-sdk-go/service/servicediscovery"
    10  	spartaAWS "github.com/mweagle/Sparta/aws"
    11  	"github.com/pkg/errors"
    12  	"github.com/sirupsen/logrus"
    13  )
    14  
    15  type discoveryInfo struct {
    16  	serviceID     string
    17  	namespaceName string
    18  	serviceName   string
    19  }
    20  
    21  var cachedInfo map[string][]*discoveryInfo
    22  
    23  func init() {
    24  	cachedInfo = make(map[string][]*discoveryInfo)
    25  }
    26  
    27  func discoveryInfoFromIDs(namespaceID string,
    28  	serviceID string,
    29  	logger *logrus.Logger) (*discoveryInfo, error) {
    30  
    31  	// Closure to enforce proper semantic return
    32  	returnWrapper := func(discoveryInfo *discoveryInfo) (*discoveryInfo, error) {
    33  		if discoveryInfo.namespaceName != "" &&
    34  			discoveryInfo.serviceName != "" {
    35  			return discoveryInfo, nil
    36  		}
    37  		return nil, errors.Errorf("Failed to lookup (%s, %s) namespaceID, serviceID pair",
    38  			namespaceID,
    39  			serviceID)
    40  	}
    41  	// Start the lookup logic
    42  	existingInfo, existingInfoOk := cachedInfo[namespaceID]
    43  	logger.WithFields(logrus.Fields{
    44  		"existingInfo": existingInfo,
    45  		"exists":       existingInfoOk}).Info("Cached info")
    46  
    47  	if existingInfoOk {
    48  		for _, eachDiscoveryInfo := range existingInfo {
    49  			if eachDiscoveryInfo.serviceID == serviceID {
    50  				return returnWrapper(eachDiscoveryInfo)
    51  			}
    52  		}
    53  
    54  	}
    55  	// It doesn't exist, let's see if we can get the data...
    56  	locker := sync.RWMutex{}
    57  	locker.Lock()
    58  	defer locker.Unlock()
    59  
    60  	lookupInfo := &discoveryInfo{
    61  		serviceID: serviceID,
    62  	}
    63  	// Issue the queries concurrently
    64  	var wg sync.WaitGroup
    65  	wg.Add(2)
    66  	session := spartaAWS.NewSession(logger)
    67  	cloudmapSvc := servicediscovery.New(session)
    68  
    69  	// Go get the namespace info
    70  	go func(svc *servicediscovery.ServiceDiscovery) {
    71  		defer wg.Done()
    72  
    73  		params := &servicediscovery.GetNamespaceInput{
    74  			Id: aws.String(namespaceID),
    75  		}
    76  		result, resultErr := cloudmapSvc.GetNamespace(params)
    77  		logger.WithFields(logrus.Fields{
    78  			"result":    result,
    79  			"resultErr": resultErr,
    80  		}).Debug("GetNamespace results")
    81  		if resultErr != nil {
    82  			logger.WithField("Error", resultErr).Error("Failed to lookup service")
    83  		} else {
    84  			lookupInfo.namespaceName = *result.Namespace.Name
    85  		}
    86  	}(cloudmapSvc)
    87  
    88  	// Go get the service info
    89  	go func(svc *servicediscovery.ServiceDiscovery) {
    90  		defer wg.Done()
    91  
    92  		params := &servicediscovery.GetServiceInput{
    93  			Id: aws.String(serviceID),
    94  		}
    95  		result, resultErr := cloudmapSvc.GetService(params)
    96  		logger.WithFields(logrus.Fields{
    97  			"result":    result,
    98  			"resultErr": resultErr,
    99  		}).Debug("GetService results")
   100  		if resultErr != nil {
   101  			logger.WithField("Error", resultErr).Error("Failed to lookup service")
   102  		} else {
   103  			lookupInfo.serviceName = *result.Service.Name
   104  		}
   105  	}(cloudmapSvc)
   106  	wg.Wait()
   107  
   108  	// Push it onto the end of the stack and return the value...
   109  	if existingInfo == nil {
   110  		existingInfo = make([]*discoveryInfo, 0)
   111  	}
   112  	existingInfo = append(existingInfo, lookupInfo)
   113  	cachedInfo[namespaceID] = existingInfo
   114  	return returnWrapper(lookupInfo)
   115  }
   116  
   117  // DiscoverInstances returns the HttpInstanceSummary items that match
   118  // the given attribute map
   119  func DiscoverInstances(attributes map[string]string,
   120  	logger *logrus.Logger) ([]*servicediscovery.HttpInstanceSummary, error) {
   121  	return DiscoverInstancesWithContext(context.Background(), attributes, logger)
   122  }
   123  
   124  // DiscoverInstancesWithContext returns the HttpInstanceSummary items that match
   125  // the given attribute map for the default service provisioned with this
   126  // application
   127  func DiscoverInstancesWithContext(ctx context.Context,
   128  	attributes map[string]string,
   129  	logger *logrus.Logger) ([]*servicediscovery.HttpInstanceSummary, error) {
   130  
   131  	// Get the default discovery info and translate that into name/id pairs...
   132  	namespaceID := os.Getenv(EnvVarCloudMapNamespaceID)
   133  	serviceID := os.Getenv(EnvVarCloudMapServiceID)
   134  	discoveryInfo, discoveryInfoErr := discoveryInfoFromIDs(namespaceID, serviceID, logger)
   135  
   136  	logger.WithFields(logrus.Fields{
   137  		"namespaceID":      namespaceID,
   138  		"serviceID":        serviceID,
   139  		"discoveryInfo":    discoveryInfo,
   140  		"discoveryInfoErr": discoveryInfoErr,
   141  	}).Debug("Discovery info lookup results")
   142  	if discoveryInfoErr != nil {
   143  		return nil, discoveryInfoErr
   144  	}
   145  	return DiscoverInstancesInServiceWithContext(ctx,
   146  		discoveryInfo.namespaceName,
   147  		discoveryInfo.serviceName,
   148  		attributes,
   149  		logger)
   150  }
   151  
   152  // DiscoverInstancesInServiceWithContext returns the HttpInstanceSummary items that match
   153  // the given attribute map using the supplied context and within the given ServiceID
   154  func DiscoverInstancesInServiceWithContext(ctx context.Context,
   155  	namespaceName string,
   156  	serviceName string,
   157  	attributes map[string]string,
   158  	logger *logrus.Logger) ([]*servicediscovery.HttpInstanceSummary, error) {
   159  
   160  	// Great, lookup the instances...
   161  	queryParams := make(map[string]*string)
   162  	for eachKey, eachValue := range attributes {
   163  		queryParams[eachKey] = aws.String(eachValue)
   164  	}
   165  
   166  	session := spartaAWS.NewSession(logger)
   167  	cloudmapSvc := servicediscovery.New(session)
   168  	lookupParams := &servicediscovery.DiscoverInstancesInput{
   169  		NamespaceName:   aws.String(namespaceName),
   170  		ServiceName:     aws.String(serviceName),
   171  		QueryParameters: queryParams,
   172  	}
   173  	results, resultsErr := cloudmapSvc.DiscoverInstances(lookupParams)
   174  	if resultsErr != nil {
   175  		return nil, resultsErr
   176  	}
   177  	return results.Instances, nil
   178  }