diff --git a/.go-version b/.go-version index d3de22ef..74bb9d57 100644 --- a/.go-version +++ b/.go-version @@ -1 +1 @@ -1.73.1 +1.73.2 diff --git a/client/metric_dashboards.go b/client/metric_dashboards.go index bc566c49..92e8e661 100644 --- a/client/metric_dashboards.go +++ b/client/metric_dashboards.go @@ -15,11 +15,12 @@ type UnifiedDashboard struct { } type UnifiedDashboardAttributes struct { - Name string `json:"name"` - Description string `json:"description"` - Charts []UnifiedChart `json:"charts"` - Groups []UnifiedGroup `json:"groups"` - Labels []Label `json:"labels"` + Name string `json:"name"` + Description string `json:"description"` + Charts []UnifiedChart `json:"charts"` + Groups []UnifiedGroup `json:"groups"` + Labels []Label `json:"labels"` + TemplateVariables []TemplateVariable `json:"template_variables"` } type UnifiedGroup struct { @@ -63,6 +64,12 @@ type MetricGroupBy struct { AggregationMethod string `json:"aggregation-method"` } +type TemplateVariable struct { + Name string `json:"name"` + DefaultValues []string `json:"default_values"` + SuggestionAttributeKey string `json:"suggestion_attribute_key"` +} + func getUnifiedDashboardURL(project, id string, query map[string]string) string { path := fmt.Sprintf( "projects/%s/metric_dashboards", @@ -95,10 +102,11 @@ func (c *Client) CreateUnifiedDashboard( bytes, err := json.Marshal(UnifiedDashboard{ Type: dashboard.Type, Attributes: UnifiedDashboardAttributes{ - Name: dashboard.Attributes.Name, - Description: dashboard.Attributes.Description, - Groups: dashboard.Attributes.Groups, - Labels: dashboard.Attributes.Labels, + Name: dashboard.Attributes.Name, + Description: dashboard.Attributes.Description, + Groups: dashboard.Attributes.Groups, + Labels: dashboard.Attributes.Labels, + TemplateVariables: dashboard.Attributes.TemplateVariables, }, }) diff --git a/docs/resources/dashboard.md b/docs/resources/dashboard.md index bab819ac..acb4b4d2 100644 --- a/docs/resources/dashboard.md +++ b/docs/resources/dashboard.md @@ -20,7 +20,7 @@ resource "lightstep_dashboard" "customer_charges" { dashboard_description = "Dashboard for customer charges metrics" chart { - name = "Requests by Project" + name = "Requests by Project for $service" rank = 1 type = "timeseries" @@ -28,7 +28,7 @@ resource "lightstep_dashboard" "customer_charges" { hidden = false query_name = "a" display = "line" - query_string = "metric requests | rate 10m | group_by [project_id], sum" + query_string = "metric requests | filter (service == $service) | rate 10m | group_by [project_id], sum" } } @@ -53,6 +53,12 @@ resource "lightstep_dashboard" "customer_charges" { label { value = "customlabel" } + + template_variable { + name = "service" + default_values = ["adservice"] + suggestion_attribute_key = "service_name" + } } ``` @@ -70,6 +76,7 @@ resource "lightstep_dashboard" "customer_charges" { - `dashboard_description` (String) - `group` (Block Set) (see [below for nested schema](#nestedblock--group)) - `label` (Block Set) Labels can be key/value pairs or standalone values. (see [below for nested schema](#nestedblock--label)) +- `template_variable` (Block Set) Variable to be used in dashboard queries for dynamically filtering telemetry data (see [below for nested schema](#nestedblock--template_variable)) ### Read-Only @@ -216,3 +223,13 @@ Required: Optional: - `key` (String) + + + +### Nested Schema for `template_variable` + +Required: + +- `default_values` (List of String) One or more values to set the template variable to by default (if none are provided, defaults to all possible values) +- `name` (String) Unique (per dashboard) name for template variable, beginning with a letter or underscore and only containing letters, numbers, and underscores +- `suggestion_attribute_key` (String) Attribute key used as source for suggested template variable values appearing in Lightstep UI diff --git a/docs/resources/metric_dashboard.md b/docs/resources/metric_dashboard.md index 405529ef..796ba632 100644 --- a/docs/resources/metric_dashboard.md +++ b/docs/resources/metric_dashboard.md @@ -92,6 +92,7 @@ resource "lightstep_metric_dashboard" "customer_charges" { - `dashboard_description` (String) - `group` (Block Set) (see [below for nested schema](#nestedblock--group)) - `label` (Block Set) Labels can be key/value pairs or standalone values. (see [below for nested schema](#nestedblock--label)) +- `template_variable` (Block Set) Variable to be used in dashboard queries for dynamically filtering telemetry data (see [below for nested schema](#nestedblock--template_variable)) ### Read-Only @@ -302,3 +303,13 @@ Required: Optional: - `key` (String) + + + +### Nested Schema for `template_variable` + +Required: + +- `default_values` (List of String) One or more values to set the template variable to by default (if none are provided, defaults to all possible values) +- `name` (String) Unique (per dashboard) name for template variable, beginning with a letter or underscore and only containing letters, numbers, and underscores +- `suggestion_attribute_key` (String) Attribute key used as source for suggested template variable values appearing in Lightstep UI diff --git a/lightstep/resource_dashboard_test.go b/lightstep/resource_dashboard_test.go index c2fa3343..1525142b 100644 --- a/lightstep/resource_dashboard_test.go +++ b/lightstep/resource_dashboard_test.go @@ -479,6 +479,84 @@ resource "lightstep_dashboard" "labels" { }) } +func TestAccDashboardWithTemplateVariables(t *testing.T) { + validDashboardConfigWithTemplateVariables := ` +resource "lightstep_dashboard" "test" { + project_name = "terraform-provider-tests" + dashboard_name = "Acceptance Test Dashboard with Template Variables" + chart { + name = "Chart Number One" + rank = 1 + type = "timeseries" + query { + hidden = false + query_name = "a" + display = "line" + query_string = "metric m | filter (service == $service) | rate | group_by [], sum" + } + } + template_variable { + name = "service" + default_values = ["myService"] + suggestion_attribute_key = "service_name" + } +} +` + + invalidDashboardConfigWithInvalidTemplateVariableName := ` +resource "lightstep_dashboard" "test" { + project_name = "terraform-provider-tests" + dashboard_name = "Acceptance Test Dashboard" + chart { + name = "Chart Number One" + rank = 1 + type = "timeseries" + query { + hidden = false + query_name = "a" + display = "line" + query_string = "metric m | filter (service == $invalid-name) | rate | group_by [], sum" + } + } + template_variable { + name = "invalid-name" + default_values = ["myService"] + suggestion_attribute_key = "service_name" + } +} +` + + var dashboard client.UnifiedDashboard + resourceName := "lightstep_dashboard.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testGetMetricDashboardDestroy, + Steps: []resource.TestStep{ + { + Config: validDashboardConfigWithTemplateVariables, + Check: resource.ComposeTestCheckFunc( + testAccCheckMetricDashboardExists(resourceName, &dashboard), + resource.TestCheckResourceAttr(resourceName, "dashboard_name", "Acceptance Test Dashboard with Template Variables"), + resource.TestCheckResourceAttr(resourceName, "chart.0.query.0.query_string", "metric m | filter (service == $service) | rate | group_by [], sum"), + resource.TestCheckResourceAttr(resourceName, "chart.0.query.0.display", "line"), + resource.TestCheckResourceAttr(resourceName, "chart.0.query.0.hidden", "false"), + resource.TestCheckResourceAttr(resourceName, "template_variable.0.name", "service"), + resource.TestCheckResourceAttr(resourceName, "template_variable.0.default_values.0", "myService"), + resource.TestCheckResourceAttr(resourceName, "template_variable.0.suggestion_attribute_key", "service_name"), + ), + }, + { + Config: invalidDashboardConfigWithInvalidTemplateVariableName, + Check: resource.ComposeTestCheckFunc( + testAccCheckMetricDashboardExists(resourceName, &dashboard)), + ExpectError: regexp.MustCompile("InvalidArgument"), + }, + }, + }) +} + func testGetMetricDashboardDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*client.Client) for _, r := range s.RootModule().Resources { diff --git a/lightstep/resource_metric_dashboard.go b/lightstep/resource_metric_dashboard.go index 488bee2f..1c4e8f33 100644 --- a/lightstep/resource_metric_dashboard.go +++ b/lightstep/resource_metric_dashboard.go @@ -86,6 +86,14 @@ func resourceUnifiedDashboard(chartSchemaType ChartSchemaType) *schema.Resource }, }, }, + "template_variable": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: getTemplateVariableSchema(), + }, + Description: "Variable to be used in dashboard queries for dynamically filtering telemetry data", + }, }, } } @@ -219,6 +227,29 @@ func getChartSchema(chartSchemaType ChartSchemaType) map[string]*schema.Schema { } } +func getTemplateVariableSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "Unique (per dashboard) name for template variable, beginning with a letter or underscore and only containing letters, numbers, and underscores", + }, + "suggestion_attribute_key": { + Type: schema.TypeString, + Required: true, + Description: "Attribute key used as source for suggested template variable values appearing in Lightstep UI", + }, + "default_values": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "One or more values to set the template variable to by default (if none are provided, defaults to all possible values)", + }, + } +} + type resourceUnifiedDashboardImp struct { chartSchemaType ChartSchemaType } @@ -363,11 +394,15 @@ func getUnifiedDashboardAttributesFromResource(d *schema.ResourceData) (*client. return nil, err } + templateVariableSet := d.Get("template_variable").(*schema.Set) + templateVariables := buildTemplateVariables(templateVariableSet.List()) + attributes := &client.UnifiedDashboardAttributes{ - Name: d.Get("dashboard_name").(string), - Description: d.Get("dashboard_description").(string), - Groups: groups, - Labels: labels, + Name: d.Get("dashboard_name").(string), + Description: d.Get("dashboard_description").(string), + Groups: groups, + Labels: labels, + TemplateVariables: templateVariables, } return attributes, nil @@ -519,6 +554,31 @@ func buildYAxis(yAxisIn []interface{}) (*client.YAxis, error) { return yAxis, nil } +func buildTemplateVariables(templateVariablesIn []interface{}) []client.TemplateVariable { + var newTemplateVariables []client.TemplateVariable + for _, tv := range templateVariablesIn { + tvMap := tv.(map[string]interface{}) + name := tvMap["name"].(string) + suggestionAttributeKey := tvMap["suggestion_attribute_key"].(string) + defaultValues := buildDefaultValues(tvMap["default_values"].([]interface{})) + + newTemplateVariables = append(newTemplateVariables, client.TemplateVariable{ + Name: name, + DefaultValues: defaultValues, + SuggestionAttributeKey: suggestionAttributeKey, + }) + } + return newTemplateVariables +} + +func buildDefaultValues(valuesIn []interface{}) []string { + defaultValues := make([]string, 0, len(valuesIn)) + for _, v := range valuesIn { + defaultValues = append(defaultValues, v.(string)) + } + return defaultValues +} + func (p *resourceUnifiedDashboardImp) setResourceDataFromUnifiedDashboard(project string, dash client.UnifiedDashboard, d *schema.ResourceData) error { if err := d.Set("project_name", project); err != nil { return fmt.Errorf("unable to set project_name resource field: %v", err) @@ -618,6 +678,19 @@ func (p *resourceUnifiedDashboardImp) setResourceDataFromUnifiedDashboard(projec return fmt.Errorf("unable to set labels resource field: %v", err) } + var templateVariables []interface{} + for _, tv := range dash.Attributes.TemplateVariables { + templateVariable := map[string]interface{}{} + templateVariable["name"] = tv.Name + templateVariable["default_values"] = tv.DefaultValues + templateVariable["suggestion_attribute_key"] = tv.SuggestionAttributeKey + + templateVariables = append(templateVariables, templateVariable) + } + if err := d.Set("template_variable", templateVariables); err != nil { + return fmt.Errorf("unable to set template variables resource field: %v", err) + } + return nil } diff --git a/templates/resources/dashboard.md.tmpl b/templates/resources/dashboard.md.tmpl index 0a179434..1668c21f 100644 --- a/templates/resources/dashboard.md.tmpl +++ b/templates/resources/dashboard.md.tmpl @@ -20,7 +20,7 @@ resource "lightstep_dashboard" "customer_charges" { dashboard_description = "Dashboard for customer charges metrics" chart { - name = "Requests by Project" + name = "Requests by Project for $service" rank = 1 type = "timeseries" @@ -28,7 +28,7 @@ resource "lightstep_dashboard" "customer_charges" { hidden = false query_name = "a" display = "line" - query_string = "metric requests | rate 10m | group_by [project_id], sum" + query_string = "metric requests | filter (service == $service) | rate 10m | group_by [project_id], sum" } } @@ -53,6 +53,12 @@ resource "lightstep_dashboard" "customer_charges" { label { value = "customlabel" } + + template_variable { + name = "service" + default_values = ["adservice"] + suggestion_attribute_key = "service_name" + } } ```