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  }