github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/aws/resource_aws_cloudwatch_event_rule.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"regexp"
     7  	"time"
     8  
     9  	"github.com/aws/aws-sdk-go/aws"
    10  	"github.com/aws/aws-sdk-go/aws/awserr"
    11  	events "github.com/aws/aws-sdk-go/service/cloudwatchevents"
    12  	"github.com/hashicorp/errwrap"
    13  	"github.com/hashicorp/terraform/helper/resource"
    14  	"github.com/hashicorp/terraform/helper/schema"
    15  )
    16  
    17  func resourceAwsCloudWatchEventRule() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAwsCloudWatchEventRuleCreate,
    20  		Read:   resourceAwsCloudWatchEventRuleRead,
    21  		Update: resourceAwsCloudWatchEventRuleUpdate,
    22  		Delete: resourceAwsCloudWatchEventRuleDelete,
    23  		Importer: &schema.ResourceImporter{
    24  			State: schema.ImportStatePassthrough,
    25  		},
    26  
    27  		Schema: map[string]*schema.Schema{
    28  			"name": &schema.Schema{
    29  				Type:         schema.TypeString,
    30  				Required:     true,
    31  				ForceNew:     true,
    32  				ValidateFunc: validateCloudWatchEventRuleName,
    33  			},
    34  			"schedule_expression": &schema.Schema{
    35  				Type:         schema.TypeString,
    36  				Optional:     true,
    37  				ValidateFunc: validateMaxLength(256),
    38  			},
    39  			"event_pattern": &schema.Schema{
    40  				Type:         schema.TypeString,
    41  				Optional:     true,
    42  				ValidateFunc: validateEventPatternValue(2048),
    43  				StateFunc: func(v interface{}) string {
    44  					json, _ := normalizeJsonString(v)
    45  					return json
    46  				},
    47  			},
    48  			"description": &schema.Schema{
    49  				Type:         schema.TypeString,
    50  				Optional:     true,
    51  				ValidateFunc: validateMaxLength(512),
    52  			},
    53  			"role_arn": &schema.Schema{
    54  				Type:         schema.TypeString,
    55  				Optional:     true,
    56  				ValidateFunc: validateMaxLength(1600),
    57  			},
    58  			"is_enabled": &schema.Schema{
    59  				Type:     schema.TypeBool,
    60  				Optional: true,
    61  				Default:  true,
    62  			},
    63  			"arn": &schema.Schema{
    64  				Type:     schema.TypeString,
    65  				Computed: true,
    66  			},
    67  		},
    68  	}
    69  }
    70  
    71  func resourceAwsCloudWatchEventRuleCreate(d *schema.ResourceData, meta interface{}) error {
    72  	conn := meta.(*AWSClient).cloudwatcheventsconn
    73  
    74  	input, err := buildPutRuleInputStruct(d)
    75  	if err != nil {
    76  		return errwrap.Wrapf("Creating CloudWatch Event Rule failed: {{err}}", err)
    77  	}
    78  	log.Printf("[DEBUG] Creating CloudWatch Event Rule: %s", input)
    79  
    80  	// IAM Roles take some time to propagate
    81  	var out *events.PutRuleOutput
    82  	err = resource.Retry(30*time.Second, func() *resource.RetryError {
    83  		var err error
    84  		out, err = conn.PutRule(input)
    85  		pattern := regexp.MustCompile("cannot be assumed by principal '[a-z]+\\.amazonaws\\.com'\\.$")
    86  		if err != nil {
    87  			if awsErr, ok := err.(awserr.Error); ok {
    88  				if awsErr.Code() == "ValidationException" && pattern.MatchString(awsErr.Message()) {
    89  					log.Printf("[DEBUG] Retrying creation of CloudWatch Event Rule %q", *input.Name)
    90  					return resource.RetryableError(err)
    91  				}
    92  			}
    93  			return resource.NonRetryableError(err)
    94  		}
    95  		return nil
    96  	})
    97  	if err != nil {
    98  		return errwrap.Wrapf("Creating CloudWatch Event Rule failed: {{err}}", err)
    99  	}
   100  
   101  	d.Set("arn", out.RuleArn)
   102  	d.SetId(d.Get("name").(string))
   103  
   104  	log.Printf("[INFO] CloudWatch Event Rule %q created", *out.RuleArn)
   105  
   106  	return resourceAwsCloudWatchEventRuleUpdate(d, meta)
   107  }
   108  
   109  func resourceAwsCloudWatchEventRuleRead(d *schema.ResourceData, meta interface{}) error {
   110  	conn := meta.(*AWSClient).cloudwatcheventsconn
   111  
   112  	input := events.DescribeRuleInput{
   113  		Name: aws.String(d.Id()),
   114  	}
   115  	log.Printf("[DEBUG] Reading CloudWatch Event Rule: %s", input)
   116  	out, err := conn.DescribeRule(&input)
   117  	if awsErr, ok := err.(awserr.Error); ok {
   118  		if awsErr.Code() == "ResourceNotFoundException" {
   119  			log.Printf("[WARN] Removing CloudWatch Event Rule %q because it's gone.", d.Id())
   120  			d.SetId("")
   121  			return nil
   122  		}
   123  	}
   124  	if err != nil {
   125  		return err
   126  	}
   127  	log.Printf("[DEBUG] Found Event Rule: %s", out)
   128  
   129  	d.Set("arn", out.Arn)
   130  	d.Set("description", out.Description)
   131  	if out.EventPattern != nil {
   132  		pattern, err := normalizeJsonString(*out.EventPattern)
   133  		if err != nil {
   134  			return errwrap.Wrapf("event pattern contains an invalid JSON: {{err}}", err)
   135  		}
   136  		d.Set("event_pattern", pattern)
   137  	}
   138  	d.Set("name", out.Name)
   139  	d.Set("role_arn", out.RoleArn)
   140  	d.Set("schedule_expression", out.ScheduleExpression)
   141  
   142  	boolState, err := getBooleanStateFromString(*out.State)
   143  	if err != nil {
   144  		return err
   145  	}
   146  	log.Printf("[DEBUG] Setting boolean state: %t", boolState)
   147  	d.Set("is_enabled", boolState)
   148  
   149  	return nil
   150  }
   151  
   152  func resourceAwsCloudWatchEventRuleUpdate(d *schema.ResourceData, meta interface{}) error {
   153  	conn := meta.(*AWSClient).cloudwatcheventsconn
   154  
   155  	if d.HasChange("is_enabled") && d.Get("is_enabled").(bool) {
   156  		log.Printf("[DEBUG] Enabling CloudWatch Event Rule %q", d.Id())
   157  		_, err := conn.EnableRule(&events.EnableRuleInput{
   158  			Name: aws.String(d.Id()),
   159  		})
   160  		if err != nil {
   161  			return err
   162  		}
   163  		log.Printf("[DEBUG] CloudWatch Event Rule (%q) enabled", d.Id())
   164  	}
   165  
   166  	input, err := buildPutRuleInputStruct(d)
   167  	if err != nil {
   168  		return errwrap.Wrapf("Updating CloudWatch Event Rule failed: {{err}}", err)
   169  	}
   170  	log.Printf("[DEBUG] Updating CloudWatch Event Rule: %s", input)
   171  
   172  	// IAM Roles take some time to propagate
   173  	err = resource.Retry(30*time.Second, func() *resource.RetryError {
   174  		_, err := conn.PutRule(input)
   175  		pattern := regexp.MustCompile("cannot be assumed by principal '[a-z]+\\.amazonaws\\.com'\\.$")
   176  		if err != nil {
   177  			if awsErr, ok := err.(awserr.Error); ok {
   178  				if awsErr.Code() == "ValidationException" && pattern.MatchString(awsErr.Message()) {
   179  					log.Printf("[DEBUG] Retrying update of CloudWatch Event Rule %q", *input.Name)
   180  					return resource.RetryableError(err)
   181  				}
   182  			}
   183  			return resource.NonRetryableError(err)
   184  		}
   185  		return nil
   186  	})
   187  	if err != nil {
   188  		return errwrap.Wrapf("Updating CloudWatch Event Rule failed: {{err}}", err)
   189  	}
   190  
   191  	if d.HasChange("is_enabled") && !d.Get("is_enabled").(bool) {
   192  		log.Printf("[DEBUG] Disabling CloudWatch Event Rule %q", d.Id())
   193  		_, err := conn.DisableRule(&events.DisableRuleInput{
   194  			Name: aws.String(d.Id()),
   195  		})
   196  		if err != nil {
   197  			return err
   198  		}
   199  		log.Printf("[DEBUG] CloudWatch Event Rule (%q) disabled", d.Id())
   200  	}
   201  
   202  	return resourceAwsCloudWatchEventRuleRead(d, meta)
   203  }
   204  
   205  func resourceAwsCloudWatchEventRuleDelete(d *schema.ResourceData, meta interface{}) error {
   206  	conn := meta.(*AWSClient).cloudwatcheventsconn
   207  
   208  	log.Printf("[INFO] Deleting CloudWatch Event Rule: %s", d.Id())
   209  	_, err := conn.DeleteRule(&events.DeleteRuleInput{
   210  		Name: aws.String(d.Id()),
   211  	})
   212  	if err != nil {
   213  		return fmt.Errorf("Error deleting CloudWatch Event Rule: %s", err)
   214  	}
   215  	log.Println("[INFO] CloudWatch Event Rule deleted")
   216  
   217  	d.SetId("")
   218  
   219  	return nil
   220  }
   221  
   222  func buildPutRuleInputStruct(d *schema.ResourceData) (*events.PutRuleInput, error) {
   223  	input := events.PutRuleInput{
   224  		Name: aws.String(d.Get("name").(string)),
   225  	}
   226  	if v, ok := d.GetOk("description"); ok {
   227  		input.Description = aws.String(v.(string))
   228  	}
   229  	if v, ok := d.GetOk("event_pattern"); ok {
   230  		pattern, err := normalizeJsonString(v)
   231  		if err != nil {
   232  			return nil, errwrap.Wrapf("event pattern contains an invalid JSON: {{err}}", err)
   233  		}
   234  		input.EventPattern = aws.String(pattern)
   235  	}
   236  	if v, ok := d.GetOk("role_arn"); ok {
   237  		input.RoleArn = aws.String(v.(string))
   238  	}
   239  	if v, ok := d.GetOk("schedule_expression"); ok {
   240  		input.ScheduleExpression = aws.String(v.(string))
   241  	}
   242  
   243  	input.State = aws.String(getStringStateFromBoolean(d.Get("is_enabled").(bool)))
   244  
   245  	return &input, nil
   246  }
   247  
   248  // State is represented as (ENABLED|DISABLED) in the API
   249  func getBooleanStateFromString(state string) (bool, error) {
   250  	if state == "ENABLED" {
   251  		return true, nil
   252  	} else if state == "DISABLED" {
   253  		return false, nil
   254  	}
   255  	// We don't just blindly trust AWS as they tend to return
   256  	// unexpected values in similar cases (different casing etc.)
   257  	return false, fmt.Errorf("Failed converting state %q into boolean", state)
   258  }
   259  
   260  // State is represented as (ENABLED|DISABLED) in the API
   261  func getStringStateFromBoolean(isEnabled bool) string {
   262  	if isEnabled {
   263  		return "ENABLED"
   264  	}
   265  	return "DISABLED"
   266  }
   267  
   268  func validateEventPatternValue(length int) schema.SchemaValidateFunc {
   269  	return func(v interface{}, k string) (ws []string, errors []error) {
   270  		json, err := normalizeJsonString(v)
   271  		if err != nil {
   272  			errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err))
   273  
   274  			// Invalid JSON? Return immediately,
   275  			// there is no need to collect other
   276  			// errors.
   277  			return
   278  		}
   279  
   280  		// Check whether the normalized JSON is within the given length.
   281  		if len(json) > length {
   282  			errors = append(errors, fmt.Errorf(
   283  				"%q cannot be longer than %d characters: %q", k, length, json))
   284  		}
   285  		return
   286  	}
   287  }