diff --git a/x-pack/functionbeat/_meta/beat.reference.yml b/x-pack/functionbeat/_meta/beat.reference.yml index 50721213a792..a230bd50ed2b 100644 --- a/x-pack/functionbeat/_meta/beat.reference.yml +++ b/x-pack/functionbeat/_meta/beat.reference.yml @@ -11,9 +11,12 @@ # Configure functions to run on AWS Lambda, currently we assume that the credentials # are present in the environment to correctly create the function when using the CLI. # +# Configure which S3 bucket we should upload the lambda artifact. +functionbeat.provider.aws.deploy_bucket: "functionbeat-deploy" + functionbeat.provider.aws.functions: # Define the list of function availables, each function required to have a unique name. - - name: fn_cloudwatch_logs + - name: cloudwatch type: cloudwatch_logs # Description of the method to help identify them when you run multiples functions. @@ -40,8 +43,8 @@ functionbeat.provider.aws.functions: # List of cloudwatch log group registered to that function. triggers: - - log_group_name: /aws/lambda/functionbeat-cloudwatch_logs - filter_pattern: mylog_ + - log_group_name: /aws/lambda/functionbeat-cloudwatch + #filter_pattern: mylog_ # Define custom processors for this function. #processors: diff --git a/x-pack/functionbeat/_meta/beat.yml b/x-pack/functionbeat/_meta/beat.yml index 21610addef90..05dfbf9fb6ef 100644 --- a/x-pack/functionbeat/_meta/beat.yml +++ b/x-pack/functionbeat/_meta/beat.yml @@ -12,17 +12,21 @@ # Configure functions to run on AWS Lambda, currently we assume that the credentials # are present in the environment to correctly create the function when using the CLI. # + +# Configure which S3 bucket we should upload the lambda artifact. +functionbeat.provider.aws.deploy_bucket: "functionbeat-deploy" + functionbeat.provider.aws.functions: # Accepts events from a cloudwatch log group. - - name: fn_cloudwatch_logs + - name: cloudwatch type: cloudwatch_logs # The IAM role that the lambda will take when executing your function. role: iam # List of cloudwatch streams registered to this function. triggers: - - log_group_name: /aws/lambda/functionbeat-cloudwatch_logs + - log_group_name: /aws/lambda/functionbeat-cloudwatch filter_name: myfiltername - filter_pattern: mylog_ + #filter_pattern: mylog_ # Accepts events from a SQS queue. # - name: fn_sqs diff --git a/x-pack/functionbeat/config/config.go b/x-pack/functionbeat/config/config.go index 9ba16ac9d342..56796546bcc9 100644 --- a/x-pack/functionbeat/config/config.go +++ b/x-pack/functionbeat/config/config.go @@ -9,10 +9,16 @@ package config import ( "fmt" + "regexp" "github.com/elastic/beats/libbeat/common" ) +var ( + functionPattern = "^[A-Za-z][A-Za-z0-9\\-]{0,139}$" + functionRE = regexp.MustCompile(functionPattern) +) + // ConfigOverrides overrides the defaults provided by libbeat. var ConfigOverrides = common.MustNewConfigFrom(map[string]interface{}{ "path.data": "/tmp", @@ -41,9 +47,9 @@ type ProviderConfig struct { // FunctionConfig minimal configuration from each function. type FunctionConfig struct { - Type string `config:"type"` - Name string `config:"name"` - Enabled bool `config:"enabled"` + Type string `config:"type"` + Name functionName `config:"name"` + Enabled bool `config:"enabled"` } // DefaultConfig is the default configuration for Functionbeat. @@ -54,9 +60,26 @@ var DefaultFunctionConfig = FunctionConfig{ Enabled: true, } +type functionName string + +func (f *functionName) Unpack(s string) error { + if !functionRE.MatchString(s) { + return fmt.Errorf( + "invalid name: '%s', name must match [a-zA-Z0-9-] and be at most 140 characters", + s, + ) + } + *f = functionName(s) + return nil +} + +func (f *functionName) String() string { + return string(*f) +} + // Validate enforces that function names are unique. func (p *ProviderConfig) Validate() error { - names := make(map[string]bool) + names := make(map[functionName]bool) for _, rawfn := range p.Functions { fc := FunctionConfig{} rawfn.Unpack(&fc) diff --git a/x-pack/functionbeat/config/config_test.go b/x-pack/functionbeat/config/config_test.go index 6dc6c4d15cd1..fb616b9af10e 100644 --- a/x-pack/functionbeat/config/config_test.go +++ b/x-pack/functionbeat/config/config_test.go @@ -91,3 +91,20 @@ func TestNameMustBeUnique(t *testing.T) { }) } } + +func TestFunctionName(t *testing.T) { + t.Run("valid function name", func(t *testing.T) { + f := functionName("") + err := f.Unpack("hello-world") + if !assert.NoError(t, err) { + return + } + assert.Equal(t, functionName("hello-world"), f) + }) + + t.Run("invalid function name", func(t *testing.T) { + f := functionName("") + err := f.Unpack("hello world") + assert.Error(t, err) + }) +} diff --git a/x-pack/functionbeat/functionbeat.reference.yml b/x-pack/functionbeat/functionbeat.reference.yml index ffe01643883d..960fee60703e 100644 --- a/x-pack/functionbeat/functionbeat.reference.yml +++ b/x-pack/functionbeat/functionbeat.reference.yml @@ -11,9 +11,12 @@ # Configure functions to run on AWS Lambda, currently we assume that the credentials # are present in the environment to correctly create the function when using the CLI. # +# Configure which S3 bucket we should upload the lambda artifact. +functionbeat.provider.aws.deploy_bucket: "functionbeat-deploy" + functionbeat.provider.aws.functions: # Define the list of function availables, each function required to have a unique name. - - name: fn_cloudwatch_logs + - name: cloudwatch type: cloudwatch_logs # Description of the method to help identify them when you run multiples functions. @@ -40,8 +43,8 @@ functionbeat.provider.aws.functions: # List of cloudwatch log group registered to that function. triggers: - - log_group_name: /aws/lambda/functionbeat-cloudwatch_logs - filter_pattern: mylog_ + - log_group_name: /aws/lambda/functionbeat-cloudwatch + #filter_pattern: mylog_ # Define custom processors for this function. #processors: diff --git a/x-pack/functionbeat/functionbeat.yml b/x-pack/functionbeat/functionbeat.yml index 0bc82442fd96..99833f8d8919 100644 --- a/x-pack/functionbeat/functionbeat.yml +++ b/x-pack/functionbeat/functionbeat.yml @@ -12,17 +12,21 @@ # Configure functions to run on AWS Lambda, currently we assume that the credentials # are present in the environment to correctly create the function when using the CLI. # + +# Configure which S3 bucket we should upload the lambda artifact. +functionbeat.provider.aws.deploy_bucket: "functionbeat-deploy" + functionbeat.provider.aws.functions: # Accepts events from a cloudwatch log group. - - name: fn_cloudwatch_logs + - name: cloudwatch type: cloudwatch_logs # The IAM role that the lambda will take when executing your function. role: iam # List of cloudwatch streams registered to this function. triggers: - - log_group_name: /aws/lambda/functionbeat-cloudwatch_logs + - log_group_name: /aws/lambda/functionbeat-cloudwatch filter_name: myfiltername - filter_pattern: mylog_ + #filter_pattern: mylog_ # Accepts events from a SQS queue. # - name: fn_sqs diff --git a/x-pack/functionbeat/provider/aws/cli_manager.go b/x-pack/functionbeat/provider/aws/cli_manager.go index d45adf37bb59..6971b241f0ed 100644 --- a/x-pack/functionbeat/provider/aws/cli_manager.go +++ b/x-pack/functionbeat/provider/aws/cli_manager.go @@ -24,7 +24,6 @@ import ( const ( // AWS lambda currently support go 1.x as a runtime. runtime = "go1.x" - bucket = "functionbeat-deploy" handlerName = "functionbeat" ) @@ -47,6 +46,7 @@ type CLIManager struct { provider provider.Provider awsCfg aws.Config log *logp.Logger + config *Config } func (c *CLIManager) findFunction(name string) (installer, error) { @@ -132,7 +132,7 @@ func (c *CLIManager) template(function installer, name, templateLoc string) *clo template.Resources[prefix("")] = &AWSLambdaFunction{ AWSLambdaFunction: &cloudformation.AWSLambdaFunction{ Code: &cloudformation.AWSLambdaFunction_Code{ - S3Bucket: bucket, + S3Bucket: c.bucket(), S3Key: templateLoc, }, Description: lambdaConfig.Description, @@ -198,12 +198,12 @@ func (c *CLIManager) deployTemplate(update bool, name string) error { c.log.Debugf("Using cloudformation template:\n%s", json) executer := newExecutor(c.log) - executer.Add(newOpEnsureBucket(c.log, c.awsCfg, bucket)) - executer.Add(newOpUploadToBucket(c.log, c.awsCfg, bucket, codeLoc, content)) + executer.Add(newOpEnsureBucket(c.log, c.awsCfg, c.bucket())) + executer.Add(newOpUploadToBucket(c.log, c.awsCfg, c.bucket(), codeLoc, content)) executer.Add(newOpUploadToBucket( c.log, c.awsCfg, - bucket, + c.bucket(), "functionbeat-deployment/"+name+"/cloudformation-template-create.json", json, )) @@ -211,20 +211,20 @@ func (c *CLIManager) deployTemplate(update bool, name string) error { executer.Add(newOpUpdateCloudFormation( c.log, c.awsCfg, - "https://s3.amazonaws.com/"+bucket+"/functionbeat-deployment/"+name+"/cloudformation-template-create.json", + "https://s3.amazonaws.com/"+c.bucket()+"/functionbeat-deployment/"+name+"/cloudformation-template-create.json", c.stackName(name), )) } else { executer.Add(newOpCreateCloudFormation( c.log, c.awsCfg, - "https://s3.amazonaws.com/"+bucket+"/functionbeat-deployment/"+name+"/cloudformation-template-create.json", + "https://s3.amazonaws.com/"+c.bucket()+"/functionbeat-deployment/"+name+"/cloudformation-template-create.json", c.stackName(name), )) } executer.Add(newOpWaitCloudFormation(c.log, c.awsCfg, c.stackName(name))) - executer.Add(newOpDeleteFileBucket(c.log, c.awsCfg, bucket, codeLoc)) + executer.Add(newOpDeleteFileBucket(c.log, c.awsCfg, c.bucket(), codeLoc)) if err := executer.Execute(); err != nil { if rollbackErr := executer.Rollback(); rollbackErr != nil { @@ -278,6 +278,10 @@ func (c *CLIManager) Remove(name string) error { return nil } +func (c *CLIManager) bucket() string { + return string(c.config.DeployBucket) +} + // NewCLI returns the interface to manage function on Amazon lambda. func NewCLI( log *logp.Logger, @@ -289,7 +293,13 @@ func NewCLI( return nil, err } + config := &Config{} + if err := cfg.Unpack(config); err != nil { + return nil, err + } + return &CLIManager{ + config: config, provider: provider, awsCfg: awsCfg, log: logp.NewLogger("aws lambda cli"), diff --git a/x-pack/functionbeat/provider/aws/cloudwatch_logs.go b/x-pack/functionbeat/provider/aws/cloudwatch_logs.go index 4bf93795a26a..82f8d21e6a15 100644 --- a/x-pack/functionbeat/provider/aws/cloudwatch_logs.go +++ b/x-pack/functionbeat/provider/aws/cloudwatch_logs.go @@ -8,6 +8,8 @@ import ( "context" "encoding/json" "errors" + "fmt" + "regexp" "strconv" "strings" @@ -22,6 +24,11 @@ import ( "github.com/elastic/beats/x-pack/functionbeat/provider/aws/transformer" ) +var ( + logGroupNamePattern = "^[\\.\\-_/#A-Za-z0-9]+$" + logGroupNameRE = regexp.MustCompile(logGroupNamePattern) +) + // CloudwatchLogsConfig is the configuration for the cloudwatchlogs event type. type CloudwatchLogsConfig struct { Triggers []*CloudwatchLogsTriggerConfig `config:"triggers"` @@ -32,8 +39,8 @@ type CloudwatchLogsConfig struct { // CloudwatchLogsTriggerConfig is the configuration for the specific triggers for cloudwatch. type CloudwatchLogsTriggerConfig struct { - LogGroupName string `config:"log_group_name" validate:"nonzero,required"` - FilterPattern string `config:"filter_pattern"` + LogGroupName logGroupName `config:"log_group_name" validate:"nonzero,required"` + FilterPattern string `config:"filter_pattern"` } // Validate validates the configuration. @@ -44,6 +51,33 @@ func (cfg *CloudwatchLogsConfig) Validate() error { return nil } +// DOC: see validations rules at https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_CreateLogGroup.html +type logGroupName string + +// Unpack takes a string and validate the log group format. +func (l *logGroupName) Unpack(s string) error { + const max = 512 + const min = 1 + + if len(s) > max { + return fmt.Errorf("log group name '%s' is too long, maximum length is %d", s, max) + } + + if len(s) < min { + return fmt.Errorf("log group name too short, minimum length is %d", min) + } + + if !logGroupNameRE.MatchString(s) { + return fmt.Errorf( + "invalid characters in log group name '%s', name must match regular expression: '%s'", + s, + logGroupNamePattern, + ) + } + *l = logGroupName(s) + return nil +} + // CloudwatchLogs receives CloudwatchLogs events from a lambda function and forward the logs to // an Elasticsearch cluster. type CloudwatchLogs struct { @@ -157,7 +191,7 @@ func (c *CloudwatchLogs) Template() *cloudformation.Template { ":", cloudformation.Ref("AWS::AccountId"), ":log-group:", - trigger.LogGroupName, + string(trigger.LogGroupName), ":*", }, ), @@ -168,10 +202,10 @@ func (c *CloudwatchLogs) Template() *cloudformation.Template { } // doc: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-subscriptionfilter.html - template.Resources[prefix("SubscriptionFilter"+normalize(trigger.LogGroupName))] = &AWSLogsSubscriptionFilter{ + template.Resources[prefix("SubscriptionFilter"+normalize(string(trigger.LogGroupName)))] = &AWSLogsSubscriptionFilter{ DestinationArn: cloudformation.GetAtt(prefix(""), "Arn"), FilterPattern: trigger.FilterPattern, - LogGroupName: trigger.LogGroupName, + LogGroupName: string(trigger.LogGroupName), } } return template diff --git a/x-pack/functionbeat/provider/aws/cloudwatch_logs_test.go b/x-pack/functionbeat/provider/aws/cloudwatch_logs_test.go index 080f497f712b..eb0dc1e0c219 100644 --- a/x-pack/functionbeat/provider/aws/cloudwatch_logs_test.go +++ b/x-pack/functionbeat/provider/aws/cloudwatch_logs_test.go @@ -119,3 +119,34 @@ func generateCloudwatchLogRawEvent() events.CloudwatchLogsEvent { }, } } + +func TestLogGroupName(t *testing.T) { + t.Run("valid name", func(t *testing.T) { + l := logGroupName("") + err := l.Unpack("helloworld") + if !assert.NoError(t, err) { + return + } + + assert.Equal(t, logGroupName("helloworld"), l) + }) + + t.Run("fail if contains invalid chars", func(t *testing.T) { + l := logGroupName("") + err := l.Unpack("hello world") + assert.Error(t, err) + }) + + t.Run("fail if too short", func(t *testing.T) { + l := logGroupName("") + err := l.Unpack("") + assert.Error(t, err) + }) + + t.Run("fail if above 512 chars", func(t *testing.T) { + r, _ := common.RandomBytes(513) + l := logGroupName("") + err := l.Unpack(string(r[:513])) + assert.Error(t, err) + }) +} diff --git a/x-pack/functionbeat/provider/aws/config.go b/x-pack/functionbeat/provider/aws/config.go index 13f00fb5188d..2d94135acd15 100644 --- a/x-pack/functionbeat/provider/aws/config.go +++ b/x-pack/functionbeat/provider/aws/config.go @@ -14,6 +14,11 @@ import ( "github.com/elastic/beats/libbeat/common/cfgwarn" ) +// Config expose the configuration option the AWS provider. +type Config struct { + DeployBucket bucket `config:"deploy_bucket" validate:"nonzero,required"` +} + // maxMegabytes maximums memory that a lambda can use. const maxMegabytes = 3008 @@ -84,3 +89,22 @@ func isRawBytes(v string) bool { } return true } + +type bucket string + +// Do some high level validation on the bucket name, they have strict validations on the name on the API side. +// DOC: https://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html#bucketnamingrules +func (b *bucket) Unpack(s string) error { + const max = 63 + const min = 3 + if len(s) > max { + return fmt.Errorf("bucket name '%s' is too long, name are restricted to %d chars", s, max) + } + + if len(s) < min { + return fmt.Errorf("bucket name '%s' is too short, name need to be at least %d chars long", s, min) + } + + *b = bucket(s) + return nil +} diff --git a/x-pack/functionbeat/provider/aws/config_test.go b/x-pack/functionbeat/provider/aws/config_test.go index cf78be4cee0d..715c0288e376 100644 --- a/x-pack/functionbeat/provider/aws/config_test.go +++ b/x-pack/functionbeat/provider/aws/config_test.go @@ -45,3 +45,26 @@ func TestMemSizeFactor64(t *testing.T) { assert.Equal(t, 128, v.Megabytes()) }) } + +func TestBucket(t *testing.T) { + t.Run("valid bucket name", func(t *testing.T) { + b := bucket("") + err := b.Unpack("hello-bucket") + if !assert.NoError(t, err) { + return + } + assert.Equal(t, bucket("hello-bucket"), b) + }) + + t.Run("too long", func(t *testing.T) { + b := bucket("") + err := b.Unpack("hello-bucket-abc12345566789012345678901234567890123456789012345678901234567890") + assert.Error(t, err) + }) + + t.Run("too short", func(t *testing.T) { + b := bucket("") + err := b.Unpack("he") + assert.Error(t, err) + }) +} diff --git a/x-pack/functionbeat/provider/aws/op_ensure_bucket.go b/x-pack/functionbeat/provider/aws/op_ensure_bucket.go index 8d974e4585c5..0d5aac689fa3 100644 --- a/x-pack/functionbeat/provider/aws/op_ensure_bucket.go +++ b/x-pack/functionbeat/provider/aws/op_ensure_bucket.go @@ -5,12 +5,18 @@ package aws import ( + "fmt" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/awserr" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/elastic/beats/libbeat/logp" ) +// This error is not provided by the S3 error package. +const notFound = "NotFound" + type opEnsureBucket struct { log *logp.Logger svc *s3.S3 @@ -22,21 +28,28 @@ func newOpEnsureBucket(log *logp.Logger, cfg aws.Config, bucketName string) *opE } func (o *opEnsureBucket) Execute() error { - o.log.Debugf("Creating S3 bucket: %s", o.bucketName) + o.log.Debugf("Verifying presence of S3 bucket: %s", o.bucketName) check := &s3.HeadBucketInput{Bucket: aws.String(o.bucketName)} reqCheck := o.svc.HeadBucketRequest(check) _, err := reqCheck.Send() - // bucket do not exist lets create it. - if err != nil { - input := &s3.CreateBucketInput{Bucket: aws.String(o.bucketName)} - req := o.svc.CreateBucketRequest(input) - resp, err := req.Send() - if err != nil { - o.log.Debugf("Could not create bucket, resp: %v", resp) - return err + if err == nil { + // The bucket exists and we have permission to access it. + return nil + } + + if aerr, ok := err.(awserr.Error); ok { + if aerr.Code() == notFound { + // bucket do not exist let's create it. + input := &s3.CreateBucketInput{Bucket: aws.String(o.bucketName)} + req := o.svc.CreateBucketRequest(input) + resp, err := req.Send() + if err != nil { + o.log.Debugf("Could not create bucket, resp: %v", resp) + return err + } } } - return nil + return fmt.Errorf("bucket '%s' already exist and you don't have permission to access it", o.bucketName) } diff --git a/x-pack/functionbeat/provider/aws/op_wait_cloud_formation.go b/x-pack/functionbeat/provider/aws/op_wait_cloud_formation.go index aecbd13dc786..fee4bd430bb1 100644 --- a/x-pack/functionbeat/provider/aws/op_wait_cloud_formation.go +++ b/x-pack/functionbeat/provider/aws/op_wait_cloud_formation.go @@ -6,7 +6,6 @@ package aws import ( "fmt" - "strings" "time" "github.com/aws/aws-sdk-go-v2/aws" @@ -39,21 +38,35 @@ func (o *opCloudWaitCloudFormation) Execute() error { o.log.Debug("Waiting for cloudformation confirmation") status, reason, err := queryStackStatus(o.svc, o.stackName) - for strings.Index(string(*status), "FAILED") == -1 && *status != cloudformation.StackStatusUpdateComplete && *status != cloudformation.StackStatusCreateComplete && err == nil { + // List of States from the cloud formation API. + // https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_Stack.html + for { + o.log.Debugf( + "Retrieving information on stack '%s' from cloudformation, current status: %v", + o.stackName, + *status, + ) + + if err != nil { + return err + } + + switch *status { + case cloudformation.StackStatusUpdateComplete: // OK + return nil + case cloudformation.StackStatusCreateComplete: // OK + return nil + case cloudformation.StackStatusCreateFailed: + return fmt.Errorf("failed to create the stack '%s', reason: %v", o.stackName, reason) + case cloudformation.StackStatusRollbackFailed: + return fmt.Errorf("failed to create and rollback the stack '%s', reason: %v", o.stackName, reason) + case cloudformation.StackStatusRollbackComplete: + return fmt.Errorf("failed to create the stack '%s', reason: %v", o.stackName, reason) + } + select { case <-time.After(periodicCheck): status, reason, err = queryStackStatus(o.svc, o.stackName) } } - - // Multiple status, setup a catch all for all errors. - if strings.Index(string(*status), "FAILED") != -1 { - return fmt.Errorf("Could not create the stack, status: %s, reason: %s", *status, reason) - } - - if err != nil { - return err - } - - return nil } diff --git a/x-pack/functionbeat/provider/aws/op_wait_delete_cloud_formation.go b/x-pack/functionbeat/provider/aws/op_wait_delete_cloud_formation.go index c611bfbf8bff..3f3d46db79f9 100644 --- a/x-pack/functionbeat/provider/aws/op_wait_delete_cloud_formation.go +++ b/x-pack/functionbeat/provider/aws/op_wait_delete_cloud_formation.go @@ -23,44 +23,55 @@ type opWaitDeleteCloudFormation struct { func (o *opWaitDeleteCloudFormation) Execute() error { o.log.Debug("Waiting for cloudformation delete confirmation") - status, _, err := queryStackStatus(o.svc, o.stackName) + status, reason, err := queryStackStatus(o.svc, o.stackName) - for err == nil && strings.Index(string(*status), "FAILED") == -1 { - select { - case <-time.After(periodicCheck): - status, _, err = queryStackStatus(o.svc, o.stackName) + // List of States from the cloud formation API. + // https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_Stack.html + for { + if err != nil { + // Timing its possible that the stack doesn't exist at that point. + if strings.Index(err.Error(), fmt.Sprintf("Stack with id %s does not exist", o.stackName)) != -1 { + return nil + } + return err } - } - // Since most of the type used by the AWS framework are generated from a schema definition - // I have no other way to detect that the stack is deleted. - if strings.Index(err.Error(), "Stack with id "+o.stackName+" does not exist") != -1 { - return nil - } + o.log.Debugf( + "Retrieving information on stack '%s' from cloudformation, current status: %v", + o.stackName, + *status, + ) - if err != nil { - return err - } + switch *status { + case cloudformation.StackStatusDeleteComplete: // OK + return nil + case cloudformation.StackStatusDeleteFailed: + return fmt.Errorf("failed to delete the stack '%s', reason: %v", o.stackName, reason) + case cloudformation.StackStatusRollbackFailed: + return fmt.Errorf("failed to delete and rollback the stack '%s', reason: %v", o.stackName, reason) + case cloudformation.StackStatusRollbackComplete: + return fmt.Errorf("failed to delete the stack '%s', reason: %v", o.stackName, reason) + } - return nil + select { + case <-time.After(periodicCheck): + status, reason, err = queryStackStatus(o.svc, o.stackName) + } + } } func newWaitDeleteCloudFormation(log *logp.Logger, cfg aws.Config, stackName string) *opWaitDeleteCloudFormation { return &opWaitDeleteCloudFormation{log: log, svc: cloudformation.New(cfg), stackName: stackName} } -func queryStackStatus(svc *cloudformation.CloudFormation, stackName string) (*cloudformation.StackStatus, string, error) { +func queryStackStatus(svc *cloudformation.CloudFormation, stackName string) (*cloudformation.StackStatus, *string, error) { input := &cloudformation.DescribeStacksInput{StackName: aws.String(stackName)} req := svc.DescribeStacksRequest(input) resp, err := req.Send() if err != nil { - return nil, "", err - } - - if len(resp.Stacks) == 0 { - return nil, "", fmt.Errorf("no stack found with the name %s", stackName) + return nil, nil, err } stack := resp.Stacks[0] - return &stack.StackStatus, "", nil + return &stack.StackStatus, stack.StackStatusReason, nil } diff --git a/x-pack/functionbeat/provider/registry.go b/x-pack/functionbeat/provider/registry.go index 9921ea21d5dc..a1de06fbc9b4 100644 --- a/x-pack/functionbeat/provider/registry.go +++ b/x-pack/functionbeat/provider/registry.go @@ -109,7 +109,7 @@ func CreateFunctions( return nil, err } - if strInSlice(enabledFunctions, c.Name) == -1 { + if strInSlice(enabledFunctions, c.Name.String()) == -1 { continue } @@ -160,7 +160,7 @@ func FindFunctionByName( return nil, err } - if c.Name != name { + if c.Name.String() != name { continue }