Skip to content

Commit

Permalink
Add SLO datasources
Browse files Browse the repository at this point in the history
  • Loading branch information
jharley committed Aug 20, 2023
1 parent 0925abb commit c137057
Show file tree
Hide file tree
Showing 16 changed files with 731 additions and 18 deletions.
2 changes: 1 addition & 1 deletion docs/data-sources/recipients.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ data "honeycombio_recipients" "example-dot-com" {
The following arguments are supported:

* `type` - (Optional) The type of recipient, allowed types are `email`, `pagerduty`, `slack` and `webhook`.
* `detail_filter` - (Optional) a block to further filter recipients as described below. `type` must be set when providing a filter.
* `detail_filter` - (Optional) a block to further filter recipients as described below. `name` must be set when providing a filter.

To further filter the recipient results, a `detail_filter` block can be provided which accepts the following arguments:

Expand Down
37 changes: 37 additions & 0 deletions docs/data-sources/slo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Data Source: honeycombio_slo

The `honeycombio_slo` data source retrieves the details of a single SLO.

-> **Note** Terraform will fail unless an SLO is returned by the search. Ensure that your search is specific enough to return an SLO.
If you want to match multiple SLOs, use the `honeycombio_slos` data source instead.

## Example Usage

```hcl
variable "dataset" {
type = string
}
# Retrieve the details of a single SLO
data "honeycombio_slo" "myslo" {
dataset = var.dataset
id = "fS4WfA82ACt"
}
```

## Argument Reference

The following arguments are supported:

* `dataset` - (Required) The dataset this SLO is associated with
* `id` - (Required) The ID of the SLO

## Attribute Reference

In addition to all arguments above, the following attributes are exported:

* `name` - the name of the SLO.
* `description` - the SLO's description.
* `sli` - the alias of the Derived COlumn used as the SLO's SLI.
* `target_percentage` - the percentage of qualified events expected to succeed during the `time_period`.
* `time_period` - The time period, in days, over which the SLO is evaluated.
47 changes: 47 additions & 0 deletions docs/data-sources/slos.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Data Source: honeycombio_slos

The SLOs data source allows the SLOs of a dataset to be retrieved.

## Example Usage

```hcl
variable "dataset" {
type = string
}
# returns all SLOs
data "honeycombio_slos" "all" {
dataset = var.dataset
}
# only returns the SLOs starting with 'foo_'
data "honeycombio_slos" "foo" {
dataset = var.dataset
detail_filter {
name = "name"
value_regex = "foo_*"
}
}
```

## Argument Reference

The following arguments are supported:

* `dataset` - (Required) The dataset to retrieve the SLOs list from
* `detail_filter` - (Optional) a block to further filter recipients as described below. `name` must be set when providing a filter.

To further filter the SLO results, a `detail_filter` block can be provided which accepts the following arguments:

* `name` - (Required) The name of the detail field to filter by. Currently only `name` is supported.
* `value` - (Optional) The value of the detail field to match on.
* `value_regex` - (Optional) A regular expression string to apply to the value of the detail field to match on.

~> **Note** one of `value` or `value_regex` is required.

## Attribute Reference

In addition to all arguments above, the following attributes are exported:

* `ids` - a list of all the SLO IDs found in the dataset
2 changes: 1 addition & 1 deletion honeycombio/data_source_recipients.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func dataSourceHoneycombioRecipients() *schema.Resource {
Optional: true,
MinItems: 1,
MaxItems: 1,
Description: "Attributes to filter the recipients with. `type` must be set when providing a filter.",
Description: "Attributes to filter the recipients with. `name` must be set when providing a filter.",
RequiredWith: []string{"type"},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
Expand Down
56 changes: 56 additions & 0 deletions internal/helper/filter/slo_filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package filter

import (
"fmt"
"regexp"

"github.com/honeycombio/terraform-provider-honeycombio/client"
"github.com/honeycombio/terraform-provider-honeycombio/internal/helper"
)

type SLODetailFilter struct {
Type string
Value *string
ValueRegex *regexp.Regexp
}

func NewDetailSLOFilter(filterType, v, r string) (*SLODetailFilter, error) {
if filterType != "name" {
return nil, fmt.Errorf("only name is supported as a filter type")
}
if v != "" && r != "" {
return nil, fmt.Errorf("only one of value or value_regex may be provided")
}
if v == "" && r == "" {
return nil, fmt.Errorf("one of value or value_regex must be provided")
}

var value *string
var valRegexp *regexp.Regexp
if v != "" {
value = helper.ToPtr(v)
}
if r != "" {
valRegexp = regexp.MustCompile(r)
}

return &SLODetailFilter{
Type: filterType,
Value: value,
ValueRegex: valRegexp,
}, nil
}

func (f *SLODetailFilter) Match(s client.SLO) bool {
// nil filter fails open
if f == nil {
return true
}
if f.Value != nil {
return s.Name == *f.Value
}
if f.ValueRegex != nil {
return f.ValueRegex.MatchString(s.Name)
}
return true
}
15 changes: 14 additions & 1 deletion internal/helper/hashcode/hashcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"bytes"
"fmt"
"hash/crc32"

"github.com/hashicorp/terraform-plugin-framework/types"
)

// String hashes a string to a unique hashcode.
Expand All @@ -23,7 +25,7 @@ func String(s string) int {
return 0
}

// Strings hashes a list of strings to a unique hashcode.
// Strings hashes a slice of strings to a unique hashcode.
func Strings(strings []string) string {
var buf bytes.Buffer

Expand All @@ -33,3 +35,14 @@ func Strings(strings []string) string {

return fmt.Sprintf("%d", String(buf.String()))
}

// StringValues hashes a slice of tfsdk Strings to a unique hashcode.
func StringValues(strings []types.String) string {
var buf bytes.Buffer

for _, s := range strings {
buf.WriteString(fmt.Sprintf("%s-", s.String()))
}

return fmt.Sprintf("%d", String(buf.String()))
}
6 changes: 6 additions & 0 deletions internal/helper/ptr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package helper

// Returns a pointer to the given value
func ToPtr[T any](v T) *T {
return &v
}
44 changes: 44 additions & 0 deletions internal/helper/validation/valid_regex.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package validation

import (
"context"
"fmt"
"regexp"

"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
)

var _ validator.String = isValidRegExpValidator{}

type isValidRegExpValidator struct{}

func (v isValidRegExpValidator) Description(_ context.Context) string {
return "value must be a valid regular expression"
}

func (v isValidRegExpValidator) MarkdownDescription(ctx context.Context) string {
return v.Description(ctx)
}

func (v isValidRegExpValidator) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) {
if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() {
return
}

if _, err := regexp.Compile(request.ConfigValue.ValueString()); err != nil {
response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic(
request.Path,
v.Description(ctx),
fmt.Sprintf("%q: %s", request.ConfigValue.ValueString(), err.Error()),
))
}
}

// IsValidRegExp returns an AttributeValidator which ensures that any configured
// attribute value is a valid regular expression.
//
// Null (unconfigured) and unknown (known after apply) values are skipped.
func IsValidRegExp() validator.String {
return isValidRegExpValidator{}
}
57 changes: 57 additions & 0 deletions internal/helper/validation/valid_regex_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package validation_test

import (
"context"
"testing"

"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/honeycombio/terraform-provider-honeycombio/internal/helper/validation"
)

func Test_IsValidRegexValidator(t *testing.T) {
t.Parallel()

type testCase struct {
val types.String
expectError bool
}
tests := map[string]testCase{
"unknown": {
val: types.StringUnknown(),
},
"null": {
val: types.StringNull(),
},
"valid regex": {
val: types.StringValue("^[a-z]+$"),
},
"invalid regex": {
val: types.StringValue("^[a-z+$"),
expectError: true,
},
}

for name, test := range tests {
name, test := name, test
t.Run(name, func(t *testing.T) {
t.Parallel()
request := validator.StringRequest{
Path: path.Root("test"),
PathExpression: path.MatchRoot("test"),
ConfigValue: test.val,
}
response := validator.StringResponse{}
validation.IsValidRegExp().ValidateString(context.TODO(), request, &response)

if !response.Diagnostics.HasError() && test.expectError {
t.Fatal("expected error, got no error")
}

if response.Diagnostics.HasError() && !test.expectError {
t.Fatalf("got unexpected error: %s", response.Diagnostics)
}
})
}
}
4 changes: 2 additions & 2 deletions internal/provider/derived_column_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ func (d *derivedColumnDataSource) Metadata(_ context.Context, req datasource.Met

func (d *derivedColumnDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Fetches the Derived Columns in a dataset",
Description: "Fetches a Derived Column in a dataset",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
},
"dataset": schema.StringAttribute{
Description: "The dataset to fetch the derived columns from. Use '__all__' to fetch Environment-wide derived columns.",
Description: "The dataset to fetch the derived column from. Use '__all__' to fetch an Environment-wide derived column.",
Required: true,
},
"alias": schema.StringAttribute{
Expand Down
19 changes: 6 additions & 13 deletions internal/provider/derived_columns_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,21 +82,14 @@ func (d *derivedColumnsDataSource) Read(ctx context.Context, req datasource.Read
return
}

if !data.StartsWith.IsNull() {
startsWith := data.StartsWith.ValueString()

for i := len(columns) - 1; i >= 0; i-- {
if !strings.HasPrefix(columns[i].Alias, startsWith) {
columns = append(columns[:i], columns[i+1:]...)
}
startsWith := data.StartsWith.ValueString()
for _, s := range columns {
if startsWith != "" && !strings.HasPrefix(s.Alias, startsWith) {
continue
}
data.Columns = append(data.Columns, types.StringValue(s.ID))
}

ids := make([]string, len(columns))
for _, dc := range columns {
data.Columns = append(data.Columns, types.StringValue(dc.Alias))
}
data.ID = types.StringValue(hashcode.Strings(ids))
data.ID = types.StringValue(hashcode.StringValues(data.Columns))

diags := resp.State.Set(ctx, &data)
resp.Diagnostics.Append(diags...)
Expand Down
2 changes: 2 additions & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ func (p *HoneycombioProvider) DataSources(ctx context.Context) []func() datasour
return []func() datasource.DataSource{
NewDerivedColumnDataSource,
NewDerivedColumnsDataSource,
NewSLODataSource,
NewSLOsDataSource,
}
}

Expand Down
Loading

0 comments on commit c137057

Please sign in to comment.