github.com/pmcatominey/terraform@v0.7.0-rc2.0.20160708105029-1401a52a5cc5/builtin/providers/aws/resource_aws_kinesis_stream.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/aws/awserr" 10 "github.com/aws/aws-sdk-go/service/kinesis" 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 13 ) 14 15 func resourceAwsKinesisStream() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceAwsKinesisStreamCreate, 18 Read: resourceAwsKinesisStreamRead, 19 Update: resourceAwsKinesisStreamUpdate, 20 Delete: resourceAwsKinesisStreamDelete, 21 22 Schema: map[string]*schema.Schema{ 23 "name": &schema.Schema{ 24 Type: schema.TypeString, 25 Required: true, 26 ForceNew: true, 27 }, 28 29 "shard_count": &schema.Schema{ 30 Type: schema.TypeInt, 31 Required: true, 32 ForceNew: true, 33 }, 34 35 "retention_period": &schema.Schema{ 36 Type: schema.TypeInt, 37 Optional: true, 38 Default: 24, 39 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 40 value := v.(int) 41 if value < 24 || value > 168 { 42 errors = append(errors, fmt.Errorf( 43 "%q must be between 24 and 168 hours", k)) 44 } 45 return 46 }, 47 }, 48 49 "arn": &schema.Schema{ 50 Type: schema.TypeString, 51 Optional: true, 52 Computed: true, 53 }, 54 "tags": tagsSchema(), 55 }, 56 } 57 } 58 59 func resourceAwsKinesisStreamCreate(d *schema.ResourceData, meta interface{}) error { 60 conn := meta.(*AWSClient).kinesisconn 61 sn := d.Get("name").(string) 62 createOpts := &kinesis.CreateStreamInput{ 63 ShardCount: aws.Int64(int64(d.Get("shard_count").(int))), 64 StreamName: aws.String(sn), 65 } 66 67 _, err := conn.CreateStream(createOpts) 68 if err != nil { 69 if awsErr, ok := err.(awserr.Error); ok { 70 return fmt.Errorf("[WARN] Error creating Kinesis Stream: \"%s\", code: \"%s\"", awsErr.Message(), awsErr.Code()) 71 } 72 return err 73 } 74 75 stateConf := &resource.StateChangeConf{ 76 Pending: []string{"CREATING"}, 77 Target: []string{"ACTIVE"}, 78 Refresh: streamStateRefreshFunc(conn, sn), 79 Timeout: 5 * time.Minute, 80 Delay: 10 * time.Second, 81 MinTimeout: 3 * time.Second, 82 } 83 84 streamRaw, err := stateConf.WaitForState() 85 if err != nil { 86 return fmt.Errorf( 87 "Error waiting for Kinesis Stream (%s) to become active: %s", 88 sn, err) 89 } 90 91 s := streamRaw.(kinesisStreamState) 92 d.SetId(s.arn) 93 d.Set("arn", s.arn) 94 d.Set("shard_count", s.shardCount) 95 96 return resourceAwsKinesisStreamUpdate(d, meta) 97 } 98 99 func resourceAwsKinesisStreamUpdate(d *schema.ResourceData, meta interface{}) error { 100 conn := meta.(*AWSClient).kinesisconn 101 102 d.Partial(true) 103 if err := setTagsKinesis(conn, d); err != nil { 104 return err 105 } 106 107 d.SetPartial("tags") 108 d.Partial(false) 109 110 if err := setKinesisRetentionPeriod(conn, d); err != nil { 111 return err 112 } 113 114 return resourceAwsKinesisStreamRead(d, meta) 115 } 116 117 func resourceAwsKinesisStreamRead(d *schema.ResourceData, meta interface{}) error { 118 conn := meta.(*AWSClient).kinesisconn 119 sn := d.Get("name").(string) 120 121 state, err := readKinesisStreamState(conn, sn) 122 if err != nil { 123 if awsErr, ok := err.(awserr.Error); ok { 124 if awsErr.Code() == "ResourceNotFoundException" { 125 d.SetId("") 126 return nil 127 } 128 return fmt.Errorf("[WARN] Error reading Kinesis Stream: \"%s\", code: \"%s\"", awsErr.Message(), awsErr.Code()) 129 } 130 return err 131 132 } 133 d.Set("arn", state.arn) 134 d.Set("shard_count", state.shardCount) 135 d.Set("retention_period", state.retentionPeriod) 136 137 // set tags 138 describeTagsOpts := &kinesis.ListTagsForStreamInput{ 139 StreamName: aws.String(sn), 140 } 141 tagsResp, err := conn.ListTagsForStream(describeTagsOpts) 142 if err != nil { 143 log.Printf("[DEBUG] Error retrieving tags for Stream: %s. %s", sn, err) 144 } else { 145 d.Set("tags", tagsToMapKinesis(tagsResp.Tags)) 146 } 147 148 return nil 149 } 150 151 func resourceAwsKinesisStreamDelete(d *schema.ResourceData, meta interface{}) error { 152 conn := meta.(*AWSClient).kinesisconn 153 sn := d.Get("name").(string) 154 _, err := conn.DeleteStream(&kinesis.DeleteStreamInput{ 155 StreamName: aws.String(sn), 156 }) 157 158 if err != nil { 159 return err 160 } 161 162 stateConf := &resource.StateChangeConf{ 163 Pending: []string{"DELETING"}, 164 Target: []string{"DESTROYED"}, 165 Refresh: streamStateRefreshFunc(conn, sn), 166 Timeout: 5 * time.Minute, 167 Delay: 10 * time.Second, 168 MinTimeout: 3 * time.Second, 169 } 170 171 _, err = stateConf.WaitForState() 172 if err != nil { 173 return fmt.Errorf( 174 "Error waiting for Stream (%s) to be destroyed: %s", 175 sn, err) 176 } 177 178 d.SetId("") 179 return nil 180 } 181 182 func setKinesisRetentionPeriod(conn *kinesis.Kinesis, d *schema.ResourceData) error { 183 sn := d.Get("name").(string) 184 185 oraw, nraw := d.GetChange("retention_period") 186 o := oraw.(int) 187 n := nraw.(int) 188 189 if n == 0 { 190 log.Printf("[DEBUG] Kinesis Stream (%q) Retention Period Not Changed", sn) 191 return nil 192 } 193 194 if n > o { 195 log.Printf("[DEBUG] Increasing %s Stream Retention Period to %d", sn, n) 196 _, err := conn.IncreaseStreamRetentionPeriod(&kinesis.IncreaseStreamRetentionPeriodInput{ 197 StreamName: aws.String(sn), 198 RetentionPeriodHours: aws.Int64(int64(n)), 199 }) 200 if err != nil { 201 return err 202 } 203 204 } else { 205 log.Printf("[DEBUG] Decreasing %s Stream Retention Period to %d", sn, n) 206 _, err := conn.DecreaseStreamRetentionPeriod(&kinesis.DecreaseStreamRetentionPeriodInput{ 207 StreamName: aws.String(sn), 208 RetentionPeriodHours: aws.Int64(int64(n)), 209 }) 210 if err != nil { 211 return err 212 } 213 } 214 215 stateConf := &resource.StateChangeConf{ 216 Pending: []string{"UPDATING"}, 217 Target: []string{"ACTIVE"}, 218 Refresh: streamStateRefreshFunc(conn, sn), 219 Timeout: 5 * time.Minute, 220 Delay: 10 * time.Second, 221 MinTimeout: 3 * time.Second, 222 } 223 224 _, err := stateConf.WaitForState() 225 if err != nil { 226 return fmt.Errorf( 227 "Error waiting for Kinesis Stream (%s) to become active: %s", 228 sn, err) 229 } 230 231 return nil 232 } 233 234 type kinesisStreamState struct { 235 arn string 236 status string 237 shardCount int 238 retentionPeriod int64 239 } 240 241 func readKinesisStreamState(conn *kinesis.Kinesis, sn string) (kinesisStreamState, error) { 242 describeOpts := &kinesis.DescribeStreamInput{ 243 StreamName: aws.String(sn), 244 } 245 246 var state kinesisStreamState 247 err := conn.DescribeStreamPages(describeOpts, func(page *kinesis.DescribeStreamOutput, last bool) (shouldContinue bool) { 248 state.arn = aws.StringValue(page.StreamDescription.StreamARN) 249 state.status = aws.StringValue(page.StreamDescription.StreamStatus) 250 state.shardCount += len(openShards(page.StreamDescription.Shards)) 251 state.retentionPeriod = aws.Int64Value(page.StreamDescription.RetentionPeriodHours) 252 return !last 253 }) 254 return state, err 255 } 256 257 func streamStateRefreshFunc(conn *kinesis.Kinesis, sn string) resource.StateRefreshFunc { 258 return func() (interface{}, string, error) { 259 state, err := readKinesisStreamState(conn, sn) 260 if err != nil { 261 if awsErr, ok := err.(awserr.Error); ok { 262 if awsErr.Code() == "ResourceNotFoundException" { 263 return 42, "DESTROYED", nil 264 } 265 return nil, awsErr.Code(), err 266 } 267 return nil, "failed", err 268 } 269 270 return state, state.status, nil 271 } 272 } 273 274 // See http://docs.aws.amazon.com/kinesis/latest/dev/kinesis-using-sdk-java-resharding-merge.html 275 func openShards(shards []*kinesis.Shard) []*kinesis.Shard { 276 var open []*kinesis.Shard 277 for _, s := range shards { 278 if s.SequenceNumberRange.EndingSequenceNumber == nil { 279 open = append(open, s) 280 } 281 } 282 283 return open 284 }