Skip to content

Commit

Permalink
Notification config datasources
Browse files Browse the repository at this point in the history
  • Loading branch information
blinktag committed Mar 1, 2024
1 parent 3dafae3 commit 79585b8
Show file tree
Hide file tree
Showing 14 changed files with 947 additions and 53 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ build:
install: build
mkdir -p ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH}
mv ${BINARY} ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH}
rm -Rf ./examples/.terraform ./examples/.terraform.lock.hcl ./examples/terraform.tfstate ./examples/create_pipeline/terraform.tfstate ./examples/create_pipeline/.terraform ./examples/create_pipeline/.terraform.lock.hcl
rm -Rf ./examples/.terraform ./examples/.terraform.lock.hcl ./examples/terraform.tfstate ./examples/**/terraform.tfstate ./examples/**/.terraform ./examples/**/.terraform.lock.hcl
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# Terraform Provider for the Steamdal eco system
# Terraform Provider for the Steamdal ecosystem

85 changes: 52 additions & 33 deletions examples/create_pipeline/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -13,42 +13,61 @@ provider "streamdal" {
connection_timeout = 10
}

resource "streamdal_pipeline" "detect_hostname" {
name = "Step 1"
step {
name = "Delete Field"
on_false {
abort="abort_current"
}
on_error {
abort="abort_all"
notification {
notification_config_ids = ["958a663a-561f-4463-acc7-d84ab2043c09"]
paths = ["object.payload"]
payload_type = "select_paths"
}
variable "notification_config_id" {
type = string

}
dynamic=false
detective {
type = "hostname"
args = [] # no args for this type
negate = false
path = "object.payload"
}
}
step {
name = "Replace Field Value Step"
dynamic=false
transform {
type = "replace_value" # TODO: can we eliminate this?
replace_value {
path = "object.payload"
value = "\"omg replaced!\""
}
}
# Change to your schema name or use default JSON
# Wildcards "*" are accepted
default = "test slack cfg"
}

data "streamdal_notification" "slack_test" {
filter {
name = "name"
values = [var.notification_config_id]
}
}

output "notification" {
value = data.streamdal_notification.slack_test
}

#resource "streamdal_pipeline" "detect_hostname" {
# name = "Step 1"
# step {
# name = "Delete Field"
# on_false {
# abort="abort_current"
# }
# on_error {
# abort="abort_all"
# notification {
# notification_config_ids = ["958a663a-561f-4463-acc7-d84ab2043c09"]
# paths = ["object.payload"]
# payload_type = "select_paths"
# }
#
# }
# dynamic=false
# detective {
# type = "hostname"
# args = [] # no args for this type
# negate = false
# path = "object.payload"
# }
# }
# step {
# name = "Replace Field Value Step"
# dynamic=false
# transform {
# type = "replace_value" # TODO: can we eliminate this?
# replace_value {
# path = "object.payload"
# value = "\"omg replaced!\""
# }
# }
# }
#}

# TODO: need a notification config data-source and resource
# TODO: need to pass the notification ID from the filter to the detect_hostname step
32 changes: 32 additions & 0 deletions examples/notification_config_filter/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
terraform {
required_providers {
streamdal = {
version = "0.1.0"
source = "streamdal.com/tf/streamdal"
}
}
}

provider "streamdal" {
token = "1234"
address = "localhost:8082"
connection_timeout = 10
}

variable "notification_config_id" {
type = string

# Wildcards "*" are accepted
default = "test slack *"
}

data "streamdal_notification" "slack_test" {
filter {
name = "name"
values = [var.notification_config_id]
}
}

output "notification" {
value = data.streamdal_notification.slack_test
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/hashicorp/terraform-plugin-docs v0.6.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0
github.com/maxbrunsfeld/counterfeiter/v6 v6.4.1
github.com/minio/pkg v1.7.5
github.com/streamdal/streamdal/libs/protos v0.1.29
google.golang.org/grpc v1.62.0
)
Expand Down
5 changes: 4 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,9 @@ github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
Expand All @@ -143,6 +144,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/maxbrunsfeld/counterfeiter/v6 v6.4.1 h1:hZD/8vBuw7x1WqRXD/WGjVjipbbo/HcDBgySYYbrUSk=
github.com/maxbrunsfeld/counterfeiter/v6 v6.4.1/go.mod h1:DK1Cjkc0E49ShgRVs5jy5ASrM15svSnem3K/hiSGD8o=
github.com/minio/pkg v1.7.5 h1:UOUJjewE5zoaDPlCMJtNx/swc1jT1ZR+IajT7hrLd44=
github.com/minio/pkg v1.7.5/go.mod h1:mEfGMTm5Z0b5EGxKNuPwyb5A2d+CC/VlUyRj6RJtIwo=
github.com/mitchellh/cli v1.1.2 h1:PvH+lL2B7IQ101xQL63Of8yFS2y+aDlsFcsqNc+u/Kw=
github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
Expand Down
57 changes: 57 additions & 0 deletions internal/provider/data_source_notification.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package provider

import (
"context"

"github.com/streamdal/terraform-provider-streamdal/streamdal"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func dataSourceNotification() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceNotificationRead,
SchemaVersion: 1,
Schema: map[string]*schema.Schema{
"filter": dataSourceFiltersSchema(),
"id": {
Description: "Notification Config ID",
Type: schema.TypeString,
Computed: true,
},
"name": {
Description: "Pipeline name",
Type: schema.TypeString,
Computed: true,
},
},
}
}

func dataSourceNotificationRead(_ context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
var diags diag.Diagnostics
var filters []*streamdal.Filter

s := m.(*streamdal.Streamdal)

if v, ok := d.GetOk("filter"); ok {
filters = buildFiltersDataSource(v.(*schema.Set))
} else {
return append(diags, diag.Diagnostic{
Severity: diag.Error,
Summary: "No filters defined",
Detail: "At least one filter must be defined",
})
}

notificationCfg, moreDiags := s.GetNotificationConfigFilter(filters)
if moreDiags.HasError() {
return append(diags, moreDiags...)
}

d.SetId(notificationCfg["id"].(string))
_ = d.Set("name", notificationCfg["name"].(string))

return diags
}
6 changes: 3 additions & 3 deletions internal/provider/data_source_pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ func dataSourcePipelineRead(_ context.Context, d *schema.ResourceData, m interfa
})
}

connection, moreDiags := s.GetPipelineFilter(filters)
pipeline, moreDiags := s.GetPipelineFilter(filters)
if moreDiags.HasError() {
return append(diags, moreDiags...)
}

d.SetId(connection["_id"].(string))
_ = d.Set("name", connection["name"].(string))
d.SetId(pipeline["id"].(string))
_ = d.Set("name", pipeline["name"].(string))

return diags
}
3 changes: 2 additions & 1 deletion internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ func New(version, apiToken string) func() *schema.Provider {
"streamdal_pipeline": resourcePipeline(),
},
DataSourcesMap: map[string]*schema.Resource{
"streamdal_pipeline": dataSourcePipeline(),
"streamdal_pipeline": dataSourcePipeline(),
"streamdal_notification": dataSourceNotification(),
},
}

Expand Down
24 changes: 12 additions & 12 deletions streamdal/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,35 @@ import (
"strings"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/minio/pkg/wildcard"
)

type Filter struct {
Name string
Values []string
}

// filterJSON takes a slice of map[string]interface{}, which represents the output of an API call to
// https://api.streamdal.com endpoint which returns a JSON array of objects(collections/schemas/etc). It then applies
// the given filters to the JSON array and returns only entries in the data param which have a field that
// matches a filter.
func filterJSON(data []map[string]interface{}, filters []*Filter) ([]map[string]interface{}, diag.Diagnostics) {
// filterJSON takes a slice of map[string]interface{}, which represents the output of an gRPC Call as JSON.
// It then applies the given filters to the JSON array and returns only entries in the data param which
// have a field that matches a filter.
func filterJSON(data map[string]interface{}, filters []*Filter) ([]map[string]interface{}, diag.Diagnostics) {
var diags diag.Diagnostics

found := make([]map[string]interface{}, 0)

for _, filter := range filters {
for _, item := range data {
row := item.(map[string]interface{})
// Can't match on non-existent keys
if _, ok := item[filter.Name]; !ok {
if _, ok := row[filter.Name]; !ok {
return nil, diag.FromErr(fmt.Errorf("%s is not a valid key", filter.Name))
}

checkVal := fmt.Sprintf("%s", item[filter.Name])
checkVal := fmt.Sprintf("%s", row[filter.Name])
for _, val := range filter.Values {
// Wildcard match
if matches(val, checkVal) {
found = append(found, item)
found = append(found, row)
}
}
}
Expand All @@ -42,10 +43,9 @@ func filterJSON(data []map[string]interface{}, filters []*Filter) ([]map[string]
}

func matches(val, checkVal string) bool {
// TODO: do we need this?
//if strings.Contains(val, "*") && wildcard.MatchSimple(val, checkVal) {
// return true
//}
if strings.Contains(val, "*") && wildcard.MatchSimple(val, checkVal) {
return true
}

if val == checkVal {
return true
Expand Down
50 changes: 49 additions & 1 deletion streamdal/streamdal.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (s *Streamdal) GetPipelineFilter(filters []*Filter) (map[string]interface{}
return nil, diag.FromErr(err)
}

raw := make([]map[string]interface{}, 0)
raw := map[string]interface{}{}
if err := json.Unmarshal(respBytes, &raw); err != nil {
return nil, append(diags, diag.Diagnostic{
Severity: diag.Error,
Expand Down Expand Up @@ -128,3 +128,51 @@ func (s *Streamdal) GetPipelineFilter(filters []*Filter) (map[string]interface{}

return pipelines[0], diags
}

func (s *Streamdal) GetNotificationConfigFilter(filters []*Filter) (map[string]interface{}, diag.Diagnostics) {
var diags diag.Diagnostics

md := metadata.New(map[string]string{"auth-token": s.Token})
ctx := metadata.NewOutgoingContext(context.Background(), md)

resp, err := s.Client.GetNotifications(ctx, &protos.GetNotificationsRequest{})
if err != nil {
return nil, diag.FromErr(err)
}

respBytes, err := json.Marshal(resp.GetNotifications())
if err != nil {
return nil, diag.FromErr(err)
}

raw := map[string]interface{}{}
if err := json.Unmarshal(respBytes, &raw); err != nil {
return nil, append(diags, diag.Diagnostic{
Severity: diag.Error,
Summary: "Failed to parse response",
Detail: err.Error(),
})
}

notificationCfgs, moreDiags := filterJSON(raw, filters)
if moreDiags.HasError() {
return nil, moreDiags
}

if len(notificationCfgs) < 1 {
// No notification config found using filter
return nil, append(diags, diag.Diagnostic{
Severity: diag.Error,
Summary: "Failed to find notification config",
Detail: "Filters: " + filterString(filters),
})
} else if len(notificationCfgs) > 1 {
// Filter must find only one notification config
return nil, append(diags, diag.Diagnostic{
Severity: diag.Error,
Summary: "Filter returned more than one notification config",
})
}

return notificationCfgs[0], diags
}
Loading

0 comments on commit 79585b8

Please sign in to comment.