Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(datasource/sites): support sites list #160

Merged
merged 6 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ override.tf.json
terraform-provider-sigsci

dist/
bin
10 changes: 7 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
clean:
rm -rf ./bin

build:
go build -o terraform-provider-sigsci
build: clean
go build -o bin/terraform-provider-sigsci
@sh -c "'$(CURDIR)/scripts/generate-dev-overrides.sh'"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This enables me to locally test the provider.


check:
terraform init
terraform plan

.PHONY: all
all: build check

lint:
Expand All @@ -23,4 +27,4 @@ sweep:
docs:
go install github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs && tfplugindocs generate

.PHONY: docs
.PHONY: clean docs test
8 changes: 8 additions & 0 deletions bin/developer_overrides.tfrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
provider_installation {

dev_overrides {
"signalsciences/sigsci" = "/Users/integralist/Code/fastly/terraform-provider-sigsci/bin"
}

direct {}
}
4 changes: 0 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,6 @@ github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/signalsciences/go-sigsci v0.1.13 h1:S8YVQ7ei0hybgWqGj0krle6GkA7l8k23VFgNP3ipQwc=
github.com/signalsciences/go-sigsci v0.1.13/go.mod h1:CXwoXk81ZwFdne6o8cnAYwxvke5kcLg7zE6Bl/e1KUo=
github.com/signalsciences/go-sigsci v0.1.14 h1:a2ucWDWDjXn93NXrVBcd7zyOMjxUcPEznTpCg0CLB+g=
github.com/signalsciences/go-sigsci v0.1.14/go.mod h1:CXwoXk81ZwFdne6o8cnAYwxvke5kcLg7zE6Bl/e1KUo=
github.com/signalsciences/go-sigsci v0.1.15 h1:Z1Wkli5NtN8OsvBBWBgXRS0LpAsmVrl1dgCPqFrKglo=
github.com/signalsciences/go-sigsci v0.1.15/go.mod h1:CXwoXk81ZwFdne6o8cnAYwxvke5kcLg7zE6Bl/e1KUo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
Expand Down
213 changes: 213 additions & 0 deletions provider/datasource_sites.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package provider

import (
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/signalsciences/go-sigsci"
)

func dataSourceSites() *schema.Resource {
return &schema.Resource{
Read: readSites,
Schema: map[string]*schema.Schema{
"filter": {
Type: schema.TypeString,
Optional: true,
Description: "Filter listed domains by either the site 'name' or 'display_name'",
},
"sites": {
Type: schema.TypeList,
Computed: true,
Description: "A list of all sites for a given corp.",
Elem: &schema.Resource{
// NOTE: The API returns multiple objects with a single 'uri' field.
// To avoid extra type complexity we flatten those hierarchies.
// These are the fields below that have the '_uri' suffix.
Schema: map[string]*schema.Schema{
"agent_anon_mode": {
Type: schema.TypeString,
Computed: true,
Description: "Agent IP anonimization mode - 'EU' or 'off'",
},
"agent_level": {
Type: schema.TypeString,
Computed: true,
Description: "Agent action level - 'block', 'log' or 'off'",
},
"agents_uri": {
Type: schema.TypeString,
Computed: true,
Description: "Reference to the site's agents",
},
"alerts_uri": {
Type: schema.TypeString,
Computed: true,
Description: "Reference to the site's alerts",
},
"analytics_events_uri": {
Type: schema.TypeString,
Computed: true,
Description: "Reference to the site's analytics events",
},
"blacklist_uri": {
Type: schema.TypeString,
Computed: true,
Description: "Reference to the site's blacklist",
},
Comment on lines +51 to +55
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOTE: The API has a blocklist field but it's not implemented in the go-sigsci API client.

"block_duration_secs": {
Type: schema.TypeInt,
Computed: true,
Description: "Duration to block an IP in seconds",
},
"block_http_code": {
Type: schema.TypeInt,
Computed: true,
Description: "HTTP response code to send when when traffic is being blocked",
},
"block_redirect_url": {
Type: schema.TypeString,
Computed: true,
Description: "URL to redirect to when blockHTTPCode is 301 or 302",
},
"created": {
Type: schema.TypeString,
Computed: true,
Description: "Created RFC3339 date time",
},
"display_name": {
Type: schema.TypeString,
Computed: true,
Description: "Display name of the site",
},
"events_uri": {
Type: schema.TypeString,
Computed: true,
Description: "Reference to the site's events",
},
"header_links_uri": {
Type: schema.TypeString,
Computed: true,
Description: "Reference to the site's header links",
},
"integrations_uri": {
Type: schema.TypeString,
Computed: true,
Description: "Reference to the site's integrations",
},
"members_uri": {
Type: schema.TypeString,
Computed: true,
Description: "Reference to the site's members",
},
"monitors_uri": {
Type: schema.TypeString,
Computed: true,
Description: "Reference to the site's monitors",
},
"name": {
Type: schema.TypeString,
Computed: true,
Description: "Identifying name of the site",
},
"redactions_uri": {
Type: schema.TypeString,
Computed: true,
Description: "Reference to the site's redactions",
},
"requests_uri": {
Type: schema.TypeString,
Computed: true,
Description: "Reference to the site's requests",
},
"suspicious_ips_uri": {
Type: schema.TypeString,
Computed: true,
Description: "Reference to the site's suspicious IPs",
},
"top_attacks_uri": {
Type: schema.TypeString,
Computed: true,
Description: "Reference to the site's top attacks",
},
"whitelist_uri": {
Type: schema.TypeString,
Computed: true,
Description: "Reference to the site's whitelist",
},
},
},
},
},
}
}

func readSites(d *schema.ResourceData, m any) error {
pm := m.(providerMetadata)
sc := pm.Client
corp := pm.Corp

// API documentation:
// https://docs.fastly.com/signalsciences/api/#_corps__corpName__sites_get
sites, err := sc.ListSites(corp)
if err != nil {
return err
}

d.SetId("list_sites")
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously I was dynamically generating a dynamic ID but opted for a static ID as I don't imagine there will be multiple instances of the sigsci_sites data source. If we do think that is a possibility then we should make this dynamically generated (possibly using something like https://github.com/google/uuid to generate the ID).


filter := d.Get("filter").(string)

return d.Set("sites", flattenSites(sites, filter))
}

// flattenSites models data into format suitable for saving to Terraform state.
func flattenSites(data []sigsci.Site, filter string) []map[string]any {
results := []map[string]any{}
if len(data) == 0 {
return results
}

for _, site := range data {
data := map[string]any{
"agent_anon_mode": site.AgentAnonMode,
"agent_level": site.AgentLevel,
"agents_uri": site.Agents["uri"],
"alerts_uri": site.Alerts["uri"],
"analytics_events_uri": site.AnalyticsEvents["uri"],
"blacklist_uri": site.Blacklist["uri"],
"block_duration_secs": site.BlockDurationSeconds,
"block_http_code": site.BlockHTTPCode,
"block_redirect_url": site.BlockRedirectURL,
"created": site.Created.String(),
"display_name": site.DisplayName,
"events_uri": site.Events["uri"],
"header_links_uri": site.HeaderLinks["uri"],
"integrations_uri": site.Integrations["uri"],
"members_uri": site.Members["uri"],
"monitors_uri": site.Monitors["uri"],
"name": site.Name,
"redactions_uri": site.Redactions["uri"],
"requests_uri": site.Requests["uri"],
"suspicious_ips_uri": site.SuspiciousIPs["uri"],
"top_attacks_uri": site.TopAttacks["uri"],
"whitelist_uri": site.Whitelist["uri"],
}

// Prune any empty values that come from the default string value in structs.
for k, v := range data {
if v == "" {
delete(data, k)
}
}
results = append(results, data)
}

if filter != "" {
for idx, site := range results {
if site["name"] == filter || site["display_name"] == filter {
return results[idx : idx+1]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation for the filter attribute is 'match only one', e.g. the first site that matches is returned.

If we want the filter to return multiple potential sites, then the implementation should be updated.

}
}
}

return results
}
63 changes: 63 additions & 0 deletions provider/datasource_sites_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package provider

import (
"fmt"
"strconv"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

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

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: `
resource "sigsci_site" "example1" {
short_name = "terraform_site1"
display_name = "terraform test example site1"
}
resource "sigsci_site" "example2" {
short_name = "terraform_site2"
display_name = "terraform test example site2"
}
data "sigsci_sites" "example" {
depends_on = [sigsci_site.example1, sigsci_site.example2]
filter = "terraform_site2"
}
`,
Check: resource.ComposeTestCheckFunc(
func(s *terraform.State) error {
r := s.RootModule().Resources["data.sigsci_sites.example"]
a := r.Primary.Attributes

sites, err := strconv.Atoi(a["sites.#"])
if err != nil {
return err
}

if sites != 1 {
return fmt.Errorf("expected one site to be returned as per the filter")
}

got := a["sites.0.name"]
want := "terraform_site2"
if got != want {
return fmt.Errorf("got: %s, want: %s", got, want)
}

return nil
},
),
ExpectNonEmptyPlan: true,
},
},
})
}
9 changes: 4 additions & 5 deletions provider/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func expandStringArray(entries *schema.Set) []string {
}

func flattenDetections(detections []sigsci.Detection) []interface{} {
var detectionsSet = make([]interface{}, len(detections))
detectionsSet := make([]interface{}, len(detections))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My editor made a bunch of automated refactors.

for i, detection := range detections {
fieldSet := make([]interface{}, len(detection.Fields))
for j, field := range detection.Fields {
Expand Down Expand Up @@ -137,7 +137,7 @@ func expandAlerts(entries *schema.Set) []sigsci.Alert {
}

func flattenAlerts(alerts []sigsci.Alert) []interface{} {
var alertsSet = make([]interface{}, len(alerts))
alertsSet := make([]interface{}, len(alerts))
for i, alert := range alerts {
alertsSet[i] = map[string]interface{}{
"id": alert.ID,
Expand Down Expand Up @@ -385,7 +385,7 @@ func expandRuleConditions(conditionsResource *schema.Set) []sigsci.Condition {
}

func flattenRuleConditions(conditions []sigsci.Condition) []interface{} {
var conditionsMap = make([]interface{}, len(conditions))
conditionsMap := make([]interface{}, len(conditions))
for i, condition := range conditions {
conditionMap := map[string]interface{}{
"type": condition.Type,
Expand Down Expand Up @@ -475,7 +475,7 @@ func flattenRuleRateLimit(rateLimit *sigsci.RateLimit) map[string]string {
}

func flattenRuleActions(actions []sigsci.Action, customResponseCode bool) []interface{} {
var actionsMap = make([]interface{}, len(actions))
actionsMap := make([]interface{}, len(actions))
for i, action := range actions {

actionMap := map[string]interface{}{
Expand Down Expand Up @@ -513,7 +513,6 @@ func resourceSiteImport(siteID string) (site string, id string, err error) {
var siteImporter = schema.ResourceImporter{
State: func(d *schema.ResourceData, i interface{}) ([]*schema.ResourceData, error) {
site, id, err := resourceSiteImport(d.Id())

if err != nil {
return nil, err
}
Expand Down
3 changes: 3 additions & 0 deletions provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ func Provider() terraform.ResourceProvider {
"sigsci_edge_deployment": resourceEdgeDeployment(),
"sigsci_edge_deployment_service": resourceEdgeDeploymentService(),
},
DataSourcesMap: map[string]*schema.Resource{
"sigsci_sites": dataSourceSites(),
},
}
provider.ConfigureFunc = providerConfigure()
return provider
Expand Down
Loading