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 @@ +
  • + XRay Resources + +
  • + <% end %> diff --git a/website/docs/guides/custom-service-endpoints.html.md b/website/docs/guides/custom-service-endpoints.html.md index eda8483e562..a56c74686ef 100644 --- a/website/docs/guides/custom-service-endpoints.html.md +++ b/website/docs/guides/custom-service-endpoints.html.md @@ -159,6 +159,7 @@ The Terraform AWS Provider allows the following endpoints to be customized: - `wafregional` - `worklink` - `workspaces` +- `xray` ## Connecting to Local AWS Compatible Solutions diff --git a/website/docs/r/xray_sampling_rule.html.markdown b/website/docs/r/xray_sampling_rule.html.markdown new file mode 100644 index 00000000000..9fc2a798a6b --- /dev/null +++ b/website/docs/r/xray_sampling_rule.html.markdown @@ -0,0 +1,62 @@ +--- +layout: "aws" +page_title: "AWS: aws_xray_sampling_rule" +sidebar_current: "docs-aws-resource-xray-sampling-rule" +description: |- + Creates and manages an AWS XRay Sampling Rule. +--- + +# Resource: aws_xray_sampling_rule + +Creates and manages an AWS XRay Sampling Rule. + +## Example Usage + +```hcl +resource "aws_xray_sampling_rule" "example" { + rule_name = "example" + priority = 10000 + version = 1 + reservoir_size = 1 + fixed_rate = 0.05, + url_path = "*" + host = "*" + http_method = "*" + service_type = "*" + service_name = "*" + resource_arn = "*" + attributes = { + Hello = "Tris" + } +} +``` + +## Argument Reference + +* `rule_name` - (Required) The name of the sampling rule. +* `resource_arn` - (Required) Matches the ARN of the AWS resource on which the service runs. +* `priority` - (Required) The priority of the sampling rule. +* `fixed_rate` - (Required) The percentage of matching requests to instrument, after the reservoir is exhausted. +* `reservoir_size` - (Required) A fixed number of matching requests to instrument per second, prior to applying the fixed rate. The reservoir is not used directly by services, but applies to all services using the rule collectively. +* `service_name` - (Required) Matches the `name` that the service uses to identify itself in segments. +* `service_type` - (Required) Matches the `origin` that the service uses to identify its type in segments. +* `host` - (Required) Matches the hostname from a request URL. +* `http_method` - (Required) Matches the HTTP method of a request. +* `url_path` - (Required) Matches the path from a request URL. +* `version` - (Required) The version of the sampling rule format (`1` ) +* `attributes` - (Optional) Matches attributes derived from the request. + +## Attributes Reference + +In addition to the arguments above, the following attributes are exported: + +* `id` - The name of the sampling rule. +* `arn` - The ARN of the sampling rule. + +## Import + +XRay Sampling Rules can be imported using the name, e.g. + +``` +$ terraform import aws_xray_sampling_rule.example example +```