[awslogs] Auto-detect region on EC2 instances

Signed-off-by: Samuel Karp <skarp@amazon.com>
This commit is contained in:
Samuel Karp 2015-09-28 06:40:44 +00:00
Родитель 480c9c0178
Коммит 8a6dfb26f3
4 изменённых файлов: 86 добавлений и 14 удалений

Просмотреть файл

@ -2,6 +2,7 @@
package awslogs
import (
"errors"
"fmt"
"os"
"runtime"
@ -14,6 +15,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/defaults"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/docker/docker/daemon/logger"
@ -58,6 +60,10 @@ type api interface {
PutLogEvents(*cloudwatchlogs.PutLogEventsInput) (*cloudwatchlogs.PutLogEventsOutput, error)
}
type regionFinder interface {
Region() (string, error)
}
type byTimestamp []*cloudwatchlogs.InputLogEvent
// init registers the awslogs driver and sets the default region, if provided
@ -85,13 +91,17 @@ func New(ctx logger.Context) (logger.Logger, error) {
if ctx.Config[logStreamKey] != "" {
logStreamName = ctx.Config[logStreamKey]
}
client, err := newAWSLogsClient(ctx)
if err != nil {
return nil, err
}
containerStream := &logStream{
logStreamName: logStreamName,
logGroupName: logGroupName,
client: newAWSLogsClient(ctx),
client: client,
messages: make(chan *logger.Message, 4096),
}
err := containerStream.create()
err = containerStream.create()
if err != nil {
return nil, err
}
@ -100,13 +110,38 @@ func New(ctx logger.Context) (logger.Logger, error) {
return containerStream, nil
}
func newAWSLogsClient(ctx logger.Context) api {
// newRegionFinder is a variable such that the implementation
// can be swapped out for unit tests.
var newRegionFinder = func() regionFinder {
return ec2metadata.New(nil)
}
// newAWSLogsClient creates the service client for Amazon CloudWatch Logs.
// Customizations to the default client from the SDK include a Docker-specific
// User-Agent string and automatic region detection using the EC2 Instance
// Metadata Service when region is otherwise unspecified.
func newAWSLogsClient(ctx logger.Context) (api, error) {
config := defaults.DefaultConfig
if ctx.Config[regionKey] != "" {
config = defaults.DefaultConfig.Merge(&aws.Config{
Region: aws.String(ctx.Config[regionKey]),
})
}
if config.Region == nil || *config.Region == "" {
logrus.Info("Trying to get region from EC2 Metadata")
ec2MetadataClient := newRegionFinder()
region, err := ec2MetadataClient.Region()
if err != nil {
logrus.WithFields(logrus.Fields{
"error": err,
}).Error("Could not get region from EC2 metadata, environment, or log option")
return nil, errors.New("Cannot determine region for awslogs driver")
}
config.Region = &region
}
logrus.WithFields(logrus.Fields{
"region": *config.Region,
}).Debug("Created awslogs client")
client := cloudwatchlogs.New(config)
client.Handlers.Build.PushBackNamed(request.NamedHandler{
@ -118,7 +153,7 @@ func newAWSLogsClient(ctx logger.Context) api {
version.VERSION, runtime.GOOS, currentAgent))
},
})
return client
return client, nil
}
// Name returns the name of the awslogs logging driver
@ -312,12 +347,6 @@ func ValidateLogOpt(cfg map[string]string) error {
if cfg[logGroupKey] == "" {
return fmt.Errorf("must specify a value for log opt '%s'", logGroupKey)
}
if cfg[regionKey] == "" && os.Getenv(regionEnvKey) == "" {
return fmt.Errorf(
"must specify a value for environment variable '%s' or log opt '%s'",
regionEnvKey,
regionKey)
}
return nil
}

Просмотреть файл

@ -32,7 +32,10 @@ func TestNewAWSLogsClientUserAgentHandler(t *testing.T) {
},
}
client := newAWSLogsClient(ctx)
client, err := newAWSLogsClient(ctx)
if err != nil {
t.Fatal(err)
}
realClient, ok := client.(*cloudwatchlogs.CloudWatchLogs)
if !ok {
t.Fatal("Could not cast client to cloudwatchlogs.CloudWatchLogs")
@ -53,6 +56,25 @@ func TestNewAWSLogsClientUserAgentHandler(t *testing.T) {
}
}
func TestNewAWSLogsClientRegionDetect(t *testing.T) {
ctx := logger.Context{
Config: map[string]string{},
}
mockMetadata := newMockMetadataClient()
newRegionFinder = func() regionFinder {
return mockMetadata
}
mockMetadata.regionResult <- &regionResult{
successResult: "us-east-1",
}
_, err := newAWSLogsClient(ctx)
if err != nil {
t.Fatal(err)
}
}
func TestCreateSuccess(t *testing.T) {
mockClient := newMockClient()
stream := &logStream{

Просмотреть файл

@ -49,6 +49,26 @@ func (m *mockcwlogsclient) PutLogEvents(input *cloudwatchlogs.PutLogEventsInput)
return output.successResult, output.errorResult
}
type mockmetadataclient struct {
regionResult chan *regionResult
}
type regionResult struct {
successResult string
errorResult error
}
func newMockMetadataClient() *mockmetadataclient {
return &mockmetadataclient{
regionResult: make(chan *regionResult, 1),
}
}
func (m *mockmetadataclient) Region() (string, error) {
output := <-m.regionResult
return output.successResult, output.errorResult
}
func test() {
_ = &logStream{
client: newMockClient(),

Просмотреть файл

@ -34,9 +34,10 @@ You can use the `--log-opt NAME=VALUE` flag to specify Amazon CloudWatch Logs lo
### awslogs-region
You must specify a region for the `awslogs` logging driver. You can specify the
region with either the `awslogs-region` log option or `AWS_REGION` environment
variable:
The `awslogs` logging driver sends your Docker logs to a specific region. Use
the `awslogs-region` log option or the `AWS_REGION` environment variable to set
the region. By default, if your Docker daemon is running on an EC2 instance
and no region is set, the driver uses the instance's region.
docker run --log-driver=awslogs --log-opt awslogs-region=us-east-1 ...