github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/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 }