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 }