<commit_before>package aws

import (
	"encoding/json"
	"fmt"
	"log"
	"regexp"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/arn"
	"github.com/aws/aws-sdk-go/aws/awsutil"
	events "github.com/aws/aws-sdk-go/service/cloudwatchevents"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/helper/validation"
)

func resourceAwsCloudWatchEventPermission() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsCloudWatchEventPermissionCreate,
		Read:   resourceAwsCloudWatchEventPermissionRead,
		Update: resourceAwsCloudWatchEventPermissionUpdate,
		Delete: resourceAwsCloudWatchEventPermissionDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"action": {
				Type:         schema.TypeString,
				Optional:     true,
				Default:      "events:PutEvents",
				ValidateFunc: validateCloudWatchEventPermissionAction,
			},
			"condition": {
				Type:     schema.TypeList,
				Optional: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"key": {
							Type:         schema.TypeString,
							Required:     true,
							ValidateFunc: validation.StringInSlice([]string{"aws:PrincipalOrgID"}, false),
						},
						"type": {
							Type:         schema.TypeString,
							Required:     true,
							ValidateFunc: validation.StringInSlice([]string{"StringEquals"}, false),
						},
						"value": {
							Type:         schema.TypeString,
							Required:     true,
							ValidateFunc: validation.NoZeroValues,
						},
					},
				},
			},
			"principal": {
				Type:         schema.TypeString,
				Required:     true,
				ValidateFunc: validateCloudWatchEventPermissionPrincipal,
			},
			"statement_id": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateCloudWatchEventPermissionStatementID,
			},
		},
	}
}

func resourceAwsCloudWatchEventPermissionCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).cloudwatcheventsconn

	statementID := d.Get("statement_id").(string)

	input := events.PutPermissionInput{
		Action:      aws.String(d.Get("action").(string)),
		Condition:   expandCloudWatchEventsCondition(d.Get("condition").([]interface{})),
		Principal:   aws.String(d.Get("principal").(string)),
		StatementId: aws.String(statementID),
	}

	log.Printf("[DEBUG] Creating CloudWatch Events permission: %s", input)
	_, err := conn.PutPermission(&input)
	if err != nil {
		return fmt.Errorf("Creating CloudWatch Events permission failed: %s", err.Error())
	}

	d.SetId(statementID)

	return resourceAwsCloudWatchEventPermissionRead(d, meta)
}

// See also: https://docs.aws.amazon.com/AmazonCloudWatchEvents/latest/APIReference/API_DescribeEventBus.html
func resourceAwsCloudWatchEventPermissionRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).cloudwatcheventsconn
	input := events.DescribeEventBusInput{}
	var policyDoc CloudWatchEventPermissionPolicyDoc
	var policyStatement *CloudWatchEventPermissionPolicyStatement

	// Especially with concurrent PutPermission calls there can be a slight delay
	var out *events.DescribeEventBusOutput
	var err error
	err = resource.Retry(1*time.Minute, func() *resource.RetryError {
		log.Printf("[DEBUG] Reading CloudWatch Events bus: %s", input)
		out, err = conn.DescribeEventBus(&input)

		if err != nil {
			return resource.NonRetryableError(fmt.Errorf("Reading CloudWatch Events permission '%s' failed: %s", d.Id(), err.Error()))
		}

		if out.Policy == nil {
			return resource.RetryableError(&resource.NotFoundError{
				Message: fmt.Sprintf("CloudWatch Events permission %q not found"+
					"in given results from DescribeEventBus", d.Id()),
				LastResponse: out,
				LastRequest:  input,
			})
		}
		return nil
	})
	if isResourceTimeoutError(err) {
		out, err = conn.DescribeEventBus(&input)
	}
	if err != nil {
		return fmt.Errorf("Reading CloudWatch Events permission '%s' failed: %s", d.Id(), err.Error())
	}

	err = json.Unmarshal([]byte(*out.Policy), &policyDoc)
	if err != nil {
		return fmt.Errorf("Reading CloudWatch Events permission '%s' failed: %s", d.Id(), err.Error())
	}

	err = resource.Retry(1*time.Minute, func() *resource.RetryError {
		policyStatement, err = findCloudWatchEventPermissionPolicyStatementByID(&policyDoc, d.Id())
		if err != nil {
			return resource.RetryableError(err)
		}
		return nil
	})
	if isResourceTimeoutError(err) {
		policyStatement, err = findCloudWatchEventPermissionPolicyStatementByID(&policyDoc, d.Id())
	}
	if err != nil {
		if nfErr, ok := err.(*resource.NotFoundError); ok {
			log.Printf("[WARN] %s", nfErr)
			d.SetId("")
			return nil
		}
		return err
	}

	d.Set("action", policyStatement.Action)

	if err := d.Set("condition", flattenCloudWatchEventPermissionPolicyStatementCondition(policyStatement.Condition)); err != nil {
		return fmt.Errorf("error setting condition: %s", err)
	}

	principalString, ok := policyStatement.Principal.(string)
	if ok && (principalString == "*") {
		d.Set("principal", "*")
	} else {
		principalMap := policyStatement.Principal.(map[string]interface{})
		policyARN, err := arn.Parse(principalMap["AWS"].(string))
		if err != nil {
			return fmt.Errorf("Reading CloudWatch Events permission '%s' failed: %s", d.Id(), err.Error())
		}
		d.Set("principal", policyARN.AccountID)
	}
	d.Set("statement_id", policyStatement.Sid)

	return nil
}

func resourceAwsCloudWatchEventPermissionUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).cloudwatcheventsconn

	input := events.PutPermissionInput{
		Action:      aws.String(d.Get("action").(string)),
		Condition:   expandCloudWatchEventsCondition(d.Get("condition").([]interface{})),
		Principal:   aws.String(d.Get("principal").(string)),
		StatementId: aws.String(d.Get("statement_id").(string)),
	}

	log.Printf("[DEBUG] Update CloudWatch Events permission: %s", input)
	_, err := conn.PutPermission(&input)
	if isAWSErr(err, events.ErrCodeResourceNotFoundException, "") {
		log.Printf("[WARN] CloudWatch Events permission %q not found, removing from state", d.Id())
		d.SetId("")
		return nil<commit_msg>Update event permission read based on code review<commit_after>package aws

import (
	"encoding/json"
	"fmt"
	"log"
	"regexp"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/arn"
	"github.com/aws/aws-sdk-go/aws/awsutil"
	events "github.com/aws/aws-sdk-go/service/cloudwatchevents"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/helper/validation"
)

func resourceAwsCloudWatchEventPermission() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsCloudWatchEventPermissionCreate,
		Read:   resourceAwsCloudWatchEventPermissionRead,
		Update: resourceAwsCloudWatchEventPermissionUpdate,
		Delete: resourceAwsCloudWatchEventPermissionDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"action": {
				Type:         schema.TypeString,
				Optional:     true,
				Default:      "events:PutEvents",
				ValidateFunc: validateCloudWatchEventPermissionAction,
			},
			"condition": {
				Type:     schema.TypeList,
				Optional: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"key": {
							Type:         schema.TypeString,
							Required:     true,
							ValidateFunc: validation.StringInSlice([]string{"aws:PrincipalOrgID"}, false),
						},
						"type": {
							Type:         schema.TypeString,
							Required:     true,
							ValidateFunc: validation.StringInSlice([]string{"StringEquals"}, false),
						},
						"value": {
							Type:         schema.TypeString,
							Required:     true,
							ValidateFunc: validation.NoZeroValues,
						},
					},
				},
			},
			"principal": {
				Type:         schema.TypeString,
				Required:     true,
				ValidateFunc: validateCloudWatchEventPermissionPrincipal,
			},
			"statement_id": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateCloudWatchEventPermissionStatementID,
			},
		},
	}
}

func resourceAwsCloudWatchEventPermissionCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).cloudwatcheventsconn

	statementID := d.Get("statement_id").(string)

	input := events.PutPermissionInput{
		Action:      aws.String(d.Get("action").(string)),
		Condition:   expandCloudWatchEventsCondition(d.Get("condition").([]interface{})),
		Principal:   aws.String(d.Get("principal").(string)),
		StatementId: aws.String(statementID),
	}

	log.Printf("[DEBUG] Creating CloudWatch Events permission: %s", input)
	_, err := conn.PutPermission(&input)
	if err != nil {
		return fmt.Errorf("Creating CloudWatch Events permission failed: %s", err.Error())
	}

	d.SetId(statementID)

	return resourceAwsCloudWatchEventPermissionRead(d, meta)
}

// See also: https://docs.aws.amazon.com/AmazonCloudWatchEvents/latest/APIReference/API_DescribeEventBus.html
func resourceAwsCloudWatchEventPermissionRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).cloudwatcheventsconn
	input := events.DescribeEventBusInput{}
	var output *events.DescribeEventBusOutput
	var policyStatement *CloudWatchEventPermissionPolicyStatement

	// Especially with concurrent PutPermission calls there can be a slight delay
	var err error
	err = resource.Retry(1*time.Minute, func() *resource.RetryError {
		log.Printf("[DEBUG] Reading CloudWatch Events bus: %s", input)
		output, err := conn.DescribeEventBus(&input)
		if err != nil {
			return resource.NonRetryableError(fmt.Errorf("Reading CloudWatch Events permission '%s' failed: %s", d.Id(), err.Error()))
		}

		policyStatement, err = getPolicyStatement(output, d.Id())
		return resource.RetryableError(err)
	})

	if isResourceTimeoutError(err) {
		output, err = conn.DescribeEventBus(&input)
		if output != nil {
			policyStatement, err = getPolicyStatement(output, d.Id())
		}
	}

	if isResourceNotFoundError(err) {
		log.Printf("[WARN] %s", err)
		d.SetId("")
		return nil
	}
	if err != nil {
		// Missing statement inside valid policy
		return err
	}

	d.Set("action", policyStatement.Action)

	if err := d.Set("condition", flattenCloudWatchEventPermissionPolicyStatementCondition(policyStatement.Condition)); err != nil {
		return fmt.Errorf("error setting condition: %s", err)
	}

	principalString, ok := policyStatement.Principal.(string)
	if ok && (principalString == "*") {
		d.Set("principal", "*")
	} else {
		principalMap := policyStatement.Principal.(map[string]interface{})
		policyARN, err := arn.Parse(principalMap["AWS"].(string))
		if err != nil {
			return fmt.Errorf("Reading CloudWatch Events permission '%s' failed: %s", d.Id(), err)
		}
		d.Set("principal", policyARN.AccountID)
	}
	d.Set("statement_id", policyStatement.Sid)

	return nil
}

func getPolicyStatement(output *events.DescribeEventBusOutput, statementID string) (*CloudWatchEventPermissionPolicyStatement, error) {
	var policyDoc CloudWatchEventPermissionPolicyDoc

	if output == nil || output.Policy == nil {
		return nil, &resource.NotFoundError{
			Message: fmt.Sprintf("CloudWatch Events permission %q not found"+
				"in given results from DescribeEventBus", statementID),
			LastResponse: output,
		}
	}

	err := json.Unmarshal([]byte(*output.Policy), &policyDoc)
	if err != nil {
		return nil, fmt.Errorf("Reading CloudWatch Events permission '%s' failed: %s", statementID, err)
	}

	return findCloudWatchEventPermissionPolicyStatementByID(&policyDoc, statementID)
}

func resourceAwsCloudWatchEventPermissionUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).cloudwatcheventsconn

	input := events.PutPermissionInput{
		Action:      aws.String(d.Get("action").(string)),
		Condition:   expandCloudWatchEventsCondition(d.Get("condition").([]interface{})),
		Principal:   aws.String(d.Get("principal").(string)),
		StatementId: aws.String(d.Get("statement_id").(string)),
	}

	log.Printf("[DEBUG] Update CloudWatch Events permission: %s", input)
	_, err := conn.PutPermission(&input)
	if isAWSErr(err, events.ErrCodeResourceNotFoundException, "") {
		log.Printf("[WARN] CloudWatch Events permission %q not found, removing from state", d.Id())
		d.SetId("")
		return nil
{'fraction_non_alphanumeric': 0.13342909584231108, 'fraction_numerical': 0.00039901045407389675, 'mean_word_length': 4.392426850258175, 'pattern_counts': {'":': 14, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5004140', 'n_tokens_mistral': 4320, 'n_tokens_neox': 3846, 'n_words': 769}