diff --git a/aws/config.go b/aws/config.go index bd969f9f7d8..9135c30eb77 100644 --- a/aws/config.go +++ b/aws/config.go @@ -123,6 +123,7 @@ import ( "github.com/aws/aws-sdk-go/service/wafregional" "github.com/aws/aws-sdk-go/service/worklink" "github.com/aws/aws-sdk-go/service/workspaces" + "github.com/aws/aws-sdk-go/service/xray" awsbase "github.com/hashicorp/aws-sdk-go-base" "github.com/hashicorp/terraform/helper/logging" "github.com/hashicorp/terraform/terraform" @@ -276,6 +277,7 @@ type AWSClient struct { wafregionalconn *wafregional.WAFRegional worklinkconn *worklink.WorkLink workspacesconn *workspaces.WorkSpaces + xrayconn *xray.XRay } // Client configures and returns a fully initialized AWSClient @@ -447,6 +449,7 @@ func (c *Config) Client() (interface{}, error) { wafregionalconn: wafregional.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["wafregional"])})), worklinkconn: worklink.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["worklink"])})), workspacesconn: workspaces.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["workspaces"])})), + xrayconn: xray.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["xray"])})), } // Handle deprecated endpoint configurations diff --git a/aws/provider.go b/aws/provider.go index 2a272e04cb1..0807a007bac 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -759,6 +759,7 @@ func Provider() terraform.ResourceProvider { "aws_pinpoint_event_stream": resourceAwsPinpointEventStream(), "aws_pinpoint_gcm_channel": resourceAwsPinpointGCMChannel(), "aws_pinpoint_sms_channel": resourceAwsPinpointSMSChannel(), + "aws_xray_sampling_rule": resourceAwsXraySamplingRule(), // ALBs are actually LBs because they can be type `network` or `application` // To avoid regressions, we will add a new resource for each and they both point @@ -961,6 +962,7 @@ func init() { "wafregional", "worklink", "workspaces", + "xray", } } diff --git a/aws/resource_aws_xray_sampling_rule.go b/aws/resource_aws_xray_sampling_rule.go new file mode 100644 index 00000000000..94f297ba472 --- /dev/null +++ b/aws/resource_aws_xray_sampling_rule.go @@ -0,0 +1,233 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/xray" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func resourceAwsXraySamplingRule() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsXraySamplingRuleCreate, + Read: resourceAwsXraySamplingRuleRead, + Update: resourceAwsXraySamplingRuleUpdate, + Delete: resourceAwsXraySamplingRuleDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "rule_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + "resource_arn": { + Type: schema.TypeString, + Required: true, + }, + "priority": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 9999), + }, + "fixed_rate": { + Type: schema.TypeFloat, + Required: true, + }, + "reservoir_size": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntAtLeast(0), + }, + "service_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(0, 64), + }, + "service_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(0, 64), + }, + "host": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(0, 64), + }, + "http_method": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(0, 10), + }, + "url_path": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(0, 128), + }, + "version": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntAtLeast(1), + }, + "attributes": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringLenBetween(1, 32), + }, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAwsXraySamplingRuleCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).xrayconn + samplingRule := &xray.SamplingRule{ + RuleName: aws.String(d.Get("rule_name").(string)), + ResourceARN: aws.String(d.Get("resource_arn").(string)), + Priority: aws.Int64(int64(d.Get("priority").(int))), + FixedRate: aws.Float64(d.Get("fixed_rate").(float64)), + ReservoirSize: aws.Int64(int64(d.Get("reservoir_size").(int))), + ServiceName: aws.String(d.Get("service_name").(string)), + ServiceType: aws.String(d.Get("service_type").(string)), + Host: aws.String(d.Get("host").(string)), + HTTPMethod: aws.String(d.Get("http_method").(string)), + URLPath: aws.String(d.Get("url_path").(string)), + Version: aws.Int64(int64(d.Get("version").(int))), + } + + if v, ok := d.GetOk("attributes"); ok { + samplingRule.Attributes = stringMapToPointers(v.(map[string]interface{})) + } + + params := &xray.CreateSamplingRuleInput{ + SamplingRule: samplingRule, + } + + out, err := conn.CreateSamplingRule(params) + if err != nil { + return fmt.Errorf("error creating XRay Sampling Rule: %s", err) + } + + d.SetId(*out.SamplingRuleRecord.SamplingRule.RuleName) + + return resourceAwsXraySamplingRuleRead(d, meta) +} + +func resourceAwsXraySamplingRuleRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).xrayconn + + samplingRule, err := getXraySamplingRule(conn, d.Id()) + + if err != nil { + return fmt.Errorf("error reading XRay Sampling Rule (%s): %s", d.Id(), err) + } + + if samplingRule == nil { + log.Printf("[WARN] XRay Sampling Rule (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + d.Set("rule_name", samplingRule.RuleName) + d.Set("resource_arn", samplingRule.ResourceARN) + d.Set("priority", samplingRule.Priority) + d.Set("fixed_rate", samplingRule.FixedRate) + d.Set("reservoir_size", samplingRule.ReservoirSize) + d.Set("service_name", samplingRule.ServiceName) + d.Set("service_type", samplingRule.ServiceType) + d.Set("host", samplingRule.Host) + d.Set("http_method", samplingRule.HTTPMethod) + d.Set("url_path", samplingRule.URLPath) + d.Set("version", samplingRule.Version) + d.Set("attributes", aws.StringValueMap(samplingRule.Attributes)) + d.Set("arn", samplingRule.RuleARN) + + return nil +} + +func resourceAwsXraySamplingRuleUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).xrayconn + samplingRuleUpdate := &xray.SamplingRuleUpdate{ + RuleName: aws.String(d.Id()), + Priority: aws.Int64(int64(d.Get("priority").(int))), + FixedRate: aws.Float64(d.Get("fixed_rate").(float64)), + ReservoirSize: aws.Int64(int64(d.Get("reservoir_size").(int))), + ServiceName: aws.String(d.Get("service_name").(string)), + ServiceType: aws.String(d.Get("service_type").(string)), + Host: aws.String(d.Get("host").(string)), + HTTPMethod: aws.String(d.Get("http_method").(string)), + URLPath: aws.String(d.Get("url_path").(string)), + } + + if d.HasChange("attributes") { + attributes := map[string]*string{} + if v, ok := d.GetOk("attributes"); ok { + if m, ok := v.(map[string]interface{}); ok { + attributes = stringMapToPointers(m) + } + } + samplingRuleUpdate.Attributes = attributes + } + + params := &xray.UpdateSamplingRuleInput{ + SamplingRuleUpdate: samplingRuleUpdate, + } + + _, err := conn.UpdateSamplingRule(params) + if err != nil { + return fmt.Errorf("error updating XRay Sampling Rule (%s): %s", d.Id(), err) + } + + return resourceAwsXraySamplingRuleRead(d, meta) +} + +func resourceAwsXraySamplingRuleDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).xrayconn + + log.Printf("[INFO] Deleting XRay Sampling Rule: %s", d.Id()) + + params := &xray.DeleteSamplingRuleInput{ + RuleName: aws.String(d.Id()), + } + _, err := conn.DeleteSamplingRule(params) + if err != nil { + return fmt.Errorf("error deleting XRay Sampling Rule: %s", d.Id()) + } + + return nil +} + +func getXraySamplingRule(conn *xray.XRay, ruleName string) (*xray.SamplingRule, error) { + params := &xray.GetSamplingRulesInput{} + for { + out, err := conn.GetSamplingRules(params) + if err != nil { + return nil, err + } + for _, samplingRuleRecord := range out.SamplingRuleRecords { + samplingRule := samplingRuleRecord.SamplingRule + if aws.StringValue(samplingRule.RuleName) == ruleName { + return samplingRule, nil + } + } + if aws.StringValue(out.NextToken) == "" { + break + } + params.NextToken = out.NextToken + } + return nil, nil +} diff --git a/aws/resource_aws_xray_sampling_rule_test.go b/aws/resource_aws_xray_sampling_rule_test.go new file mode 100644 index 00000000000..5ac78d441c3 --- /dev/null +++ b/aws/resource_aws_xray_sampling_rule_test.go @@ -0,0 +1,192 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/xray" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSXraySamplingRule_basic(t *testing.T) { + var samplingRule xray.SamplingRule + resourceName := "aws_xray_sampling_rule.test" + rString := acctest.RandString(8) + ruleName := fmt.Sprintf("tf_acc_sampling_rule_%s", rString) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSXraySamplingRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSXraySamplingRuleConfig_basic(ruleName), + Check: resource.ComposeTestCheckFunc( + testAccCheckXraySamplingRuleExists(resourceName, &samplingRule), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "xray", fmt.Sprintf("sampling-rule/%s", ruleName)), + resource.TestCheckResourceAttr(resourceName, "priority", "5"), + resource.TestCheckResourceAttr(resourceName, "version", "1"), + resource.TestCheckResourceAttr(resourceName, "reservoir_size", "10"), + resource.TestCheckResourceAttr(resourceName, "url_path", "*"), + resource.TestCheckResourceAttr(resourceName, "host", "*"), + resource.TestCheckResourceAttr(resourceName, "http_method", "GET"), + resource.TestCheckResourceAttr(resourceName, "fixed_rate", "0.3"), + resource.TestCheckResourceAttr(resourceName, "resource_arn", "*"), + resource.TestCheckResourceAttr(resourceName, "service_name", "*"), + resource.TestCheckResourceAttr(resourceName, "service_type", "*"), + resource.TestCheckResourceAttr(resourceName, "attributes.%", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSXraySamplingRule_update(t *testing.T) { + var samplingRule xray.SamplingRule + resourceName := "aws_xray_sampling_rule.test" + rString := acctest.RandString(8) + ruleName := fmt.Sprintf("tf_acc_sampling_rule_%s", rString) + updatedPriority := acctest.RandIntRange(0, 9999) + updatedReservoirSize := acctest.RandIntRange(0, 2147483647) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSXraySamplingRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSXraySamplingRuleConfig_update(ruleName, acctest.RandIntRange(0, 9999), acctest.RandIntRange(0, 2147483647)), + Check: resource.ComposeTestCheckFunc( + testAccCheckXraySamplingRuleExists(resourceName, &samplingRule), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "xray", fmt.Sprintf("sampling-rule/%s", ruleName)), + resource.TestCheckResourceAttrSet(resourceName, "priority"), + resource.TestCheckResourceAttrSet(resourceName, "reservoir_size"), + resource.TestCheckResourceAttr(resourceName, "version", "1"), + resource.TestCheckResourceAttr(resourceName, "url_path", "*"), + resource.TestCheckResourceAttr(resourceName, "host", "*"), + resource.TestCheckResourceAttr(resourceName, "http_method", "GET"), + resource.TestCheckResourceAttr(resourceName, "fixed_rate", "0.3"), + resource.TestCheckResourceAttr(resourceName, "resource_arn", "*"), + resource.TestCheckResourceAttr(resourceName, "service_name", "*"), + resource.TestCheckResourceAttr(resourceName, "service_type", "*"), + resource.TestCheckResourceAttr(resourceName, "attributes.%", "0"), + ), + }, + { // Update attributes + Config: testAccAWSXraySamplingRuleConfig_update(ruleName, updatedPriority, updatedReservoirSize), + Check: resource.ComposeTestCheckFunc( + testAccCheckXraySamplingRuleExists(resourceName, &samplingRule), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "xray", fmt.Sprintf("sampling-rule/%s", ruleName)), + resource.TestCheckResourceAttr(resourceName, "priority", fmt.Sprintf("%d", updatedPriority)), + resource.TestCheckResourceAttr(resourceName, "reservoir_size", fmt.Sprintf("%d", updatedReservoirSize)), + resource.TestCheckResourceAttr(resourceName, "version", "1"), + resource.TestCheckResourceAttr(resourceName, "url_path", "*"), + resource.TestCheckResourceAttr(resourceName, "host", "*"), + resource.TestCheckResourceAttr(resourceName, "http_method", "GET"), + resource.TestCheckResourceAttr(resourceName, "fixed_rate", "0.3"), + resource.TestCheckResourceAttr(resourceName, "resource_arn", "*"), + resource.TestCheckResourceAttr(resourceName, "service_name", "*"), + resource.TestCheckResourceAttr(resourceName, "service_type", "*"), + resource.TestCheckResourceAttr(resourceName, "attributes.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckXraySamplingRuleExists(n string, samplingRule *xray.SamplingRule) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No XRay Sampling Rule ID is set") + } + conn := testAccProvider.Meta().(*AWSClient).xrayconn + + rule, err := getXraySamplingRule(conn, rs.Primary.ID) + + if err != nil { + return err + } + + *samplingRule = *rule + + return nil + } +} + +func testAccCheckAWSXraySamplingRuleDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_xray_sampling_rule" { + continue + } + + conn := testAccProvider.Meta().(*AWSClient).xrayconn + + rule, err := getXraySamplingRule(conn, rs.Primary.ID) + + if err != nil { + return err + } + + if rule != nil { + return fmt.Errorf("Expected XRay Sampling Rule to be destroyed, %s found", rs.Primary.ID) + } + } + + return nil +} + +func testAccAWSXraySamplingRuleConfig_basic(ruleName string) string { + return fmt.Sprintf(` +resource "aws_xray_sampling_rule" "test" { + rule_name = "%s" + priority = 5 + reservoir_size = 10 + url_path = "*" + host = "*" + http_method = "GET" + service_type = "*" + service_name = "*" + fixed_rate = 0.3 + resource_arn = "*" + version = 1 + attributes = { + Hello = "World" + } +} +`, ruleName) +} + +func testAccAWSXraySamplingRuleConfig_update(ruleName string, priority int, reservoirSize int) string { + return fmt.Sprintf(` +resource "aws_xray_sampling_rule" "test" { + rule_name = "%s" + priority = %d + reservoir_size = %d + url_path = "*" + host = "*" + http_method = "GET" + service_type = "*" + service_name = "*" + fixed_rate = 0.3 + resource_arn = "*" + version = 1 +} +`, ruleName, priority, reservoirSize) +} diff --git a/website/aws.erb b/website/aws.erb index 33c096031b7..f82fc2b86e5 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -2869,6 +2869,15 @@ +