-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Read endpoints from AWS service models
Find the endpoint with the lowest latency
- Loading branch information
Showing
4 changed files
with
187 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,51 @@ | ||
package closest | ||
|
||
import ( | ||
"errors" | ||
"time" | ||
|
||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
var errEndpointsUnavailable = errors.New("service endpoints are unavailable") | ||
|
||
// Regions type selects available endpoints end finds the closest one. | ||
type Regions struct{} | ||
|
||
// Latencies type stores average latencies measured while accessing service endpoints. | ||
type Latencies map[string]time.Duration | ||
|
||
// FindClosest method finds the closest AWS region to the caller. | ||
func (r *Regions) FindClosest(endpoints Endpoints, verbose bool) (string, error) { | ||
return "us-west-66", nil | ||
func (r *Regions) FindClosest(endpoints Endpoints) (string, error) { | ||
latencies := Latencies{} | ||
for regionName, endpoint := range endpoints { | ||
latency, err := r.measureLatency(endpoint) | ||
if err == nil { | ||
latencies[regionName] = latency | ||
} | ||
} | ||
|
||
if len(latencies) == 0 { | ||
return "", errEndpointsUnavailable | ||
} | ||
return r.regionWithLowestLatency(latencies), nil | ||
} | ||
|
||
func (r *Regions) measureLatency(endpoint string) (time.Duration, error) { | ||
return 0, nil | ||
} | ||
|
||
func (r *Regions) regionWithLowestLatency(latencies Latencies) string { | ||
var theRegion string | ||
var theLatency = time.Hour | ||
|
||
for regionName, latency := range latencies { | ||
if latency < theLatency { | ||
theRegion = regionName | ||
theLatency = latency | ||
} | ||
} | ||
|
||
log.Infof(`Lowest latency was measured while accessing endpoint in the region "%s": %v`, theRegion, theLatency) | ||
return theRegion | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,70 @@ | ||
package closest | ||
|
||
import ( | ||
"errors" | ||
|
||
"github.com/aws/aws-sdk-go/aws/endpoints" | ||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
const defaultServiceName = "dynamodb" | ||
|
||
var errServiceNotAvailableInAnyRegion = errors.New("service is not available in any region") | ||
|
||
// Services type provides a list of supported AWS regions. | ||
type Services struct{} | ||
|
||
// Endpoints is a map with region name as a key and an endpoint as value. | ||
type Endpoints map[string]string | ||
|
||
// ForService method provides a list of endpoints for a given service. | ||
func (e *Endpoints) ForService(serviceName string) (Endpoints, error) { | ||
return nil, nil | ||
// EndpointsForService method provides a list of endpoints for a given service. | ||
// US-Gov partition will be skipped. | ||
func (s *Services) EndpointsForService(serviceName string) (Endpoints, error) { | ||
serviceName = s.serviceNameOrDefault(serviceName) | ||
|
||
var anyRegionExists bool | ||
serviceEndpoints := Endpoints{} | ||
for _, partition := range endpoints.DefaultPartitions() { | ||
if partition.ID() == endpoints.AwsUsGovPartition().ID() { | ||
log.Info(`Partition "us-gov" will be skipped.`) | ||
continue | ||
} | ||
|
||
serviceRegions, exists := endpoints.RegionsForService(endpoints.DefaultPartitions(), | ||
partition.ID(), serviceName) | ||
log.Infof(`Service "%s" is available in %d regions in "%s" partition.`, serviceName, | ||
len(serviceRegions), partition.ID()) | ||
anyRegionExists = anyRegionExists || exists | ||
|
||
if exists { | ||
for regionName := range serviceRegions { | ||
serviceEndpoint, err := partition.EndpointFor(serviceName, regionName) | ||
if err != nil { | ||
return nil, err | ||
} | ||
serviceEndpoints[regionName] = serviceEndpoint.URL | ||
} | ||
} | ||
} | ||
|
||
if !anyRegionExists { | ||
return nil, errServiceNotAvailableInAnyRegion | ||
} | ||
|
||
if log.IsLevelEnabled(log.InfoLevel) { | ||
log.Infoln("Service is accessing via following endpoints:") | ||
for regionName, endpoint := range serviceEndpoints { | ||
log.Infof(" %s: %s\n", regionName, endpoint) | ||
} | ||
} | ||
|
||
return serviceEndpoints, nil | ||
} | ||
|
||
func (s *Services) serviceNameOrDefault(serviceName string) string { | ||
if serviceName == "" { | ||
log.Infof("Service name hasn't been provided. Use default service name: %s", defaultServiceName) | ||
return defaultServiceName | ||
} | ||
return serviceName | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package closest | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestServices_EndpointsForService_EmptyServiceName(t *testing.T) { | ||
// given | ||
serviceName := "" | ||
services := new(Services) | ||
|
||
// when | ||
endpoints, err := services.EndpointsForService(serviceName) | ||
|
||
// then | ||
assert.Nil(t, err, "no errors should be returned") | ||
assert.True(t, len(endpoints) > 0, "at least one endpoint should be returned") | ||
assert.Contains(t, endpoints, "us-west-2") | ||
assert.NotContains(t, endpoints, "us-west-999") | ||
} | ||
|
||
func TestServices_EndpointsForService_ServiceNamePassed(t *testing.T) { | ||
// given | ||
serviceName := "polly" | ||
services := new(Services) | ||
|
||
// when | ||
endpoints, err := services.EndpointsForService(serviceName) | ||
|
||
// then | ||
assert.Nil(t, err, "no errors should be returned") | ||
assert.True(t, len(endpoints) > 0, "at least one endpoint should be returned") | ||
assert.Contains(t, endpoints, "us-west-2") | ||
assert.NotContains(t, endpoints, "us-west-999") | ||
} | ||
|
||
func TestServices_EndpointsForService_UnknownServiceName(t *testing.T) { | ||
// given | ||
serviceName := "unknown" | ||
services := new(Services) | ||
|
||
// when | ||
endpoints, err := services.EndpointsForService(serviceName) | ||
|
||
// then | ||
assert.NotNil(t, err, "an error should be returned") | ||
assert.Nil(t, endpoints, "no endpoints should be returned") | ||
} | ||
|
||
func TestServices_EndpointsForService_WithChinaPartition(t *testing.T) { | ||
// given | ||
serviceName := "dynamodb" | ||
services := new(Services) | ||
|
||
// when | ||
endpoints, err := services.EndpointsForService(serviceName) | ||
|
||
// then | ||
assert.Nil(t, err, "no errors should be returned") | ||
assert.True(t, len(endpoints) > 0, "at least one endpoint should be returned") | ||
assert.Contains(t, endpoints, "us-west-2") | ||
assert.Contains(t, endpoints, "cn-north-1") | ||
assert.Contains(t, endpoints, "cn-northwest-1") | ||
assert.NotContains(t, endpoints, "us-west-999") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters