diff --git a/client/client.go b/client/client.go
index 6a54c508..1cd9cc58 100644
--- a/client/client.go
+++ b/client/client.go
@@ -121,7 +121,7 @@ func (cli *Client) GetAuthToken() (string, string, error) {
// GetAuthToken - Connect to Aqua and return a JWT bearerToken (string)
func (cli *Client) GetCspAuthToken() (string, error) {
- resp, body, errs := cli.gorequest.Post(cli.url + "/api/v1/login").
+ resp, body, errs := cli.gorequest.Clone().Post(cli.url + "/api/v1/login").
Send(`{"id":"` + cli.user + `", "password":"` + cli.password + `"}`).End()
if errs != nil {
return "", getMergedError(errs)
diff --git a/client/suppression_rules.go b/client/suppression_rules.go
new file mode 100644
index 00000000..b52e41db
--- /dev/null
+++ b/client/suppression_rules.go
@@ -0,0 +1,305 @@
+package client
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "log"
+)
+
+type SuppressionRule struct {
+ ID int `json:"id,omitempty"`
+ Name string `json:"name,omitempty"`
+ ApplicationScopes []string `json:"application_scopes,omitempty"`
+ Scope *Scope `json:"scope,omitempty"`
+ Score []int `json:"score"`
+ Severity string `json:"severity,omitempty"`
+ FixAvailable string `json:"fix_available,omitempty"`
+ Vulnerabilities string `json:"vulnerabilities,omitempty"`
+ Expiry int `json:"expiry,omitempty"`
+ Comment string `json:"comment,omitempty"`
+ Created string `json:"created,omitempty"`
+ Author string `json:"author,omitempty"`
+ Status bool `json:"status,omitempty"`
+}
+
+type SuppressionRuleResponse struct {
+ Count int `json:"count"`
+ Page int `json:"page"`
+ PageSize int `json:"pagesize"`
+ SuppressionRules []SuppressionRule `json:"result"`
+}
+
+type CreateSuppressionRuleResponse struct {
+ ID int `json:"rule_id"`
+}
+
+func (cli *Client) GetSuppressionRules() ([]SuppressionRule, error) {
+
+ var err error
+ var response SuppressionRuleResponse
+
+ var suppressionRules []SuppressionRule
+ var totalPages = 1
+ var page = 1
+
+ for page <= totalPages {
+ request := cli.gorequest
+ apiPath := fmt.Sprintf("/api/v2/images/ack_suppression_rules/search?page=%d", page)
+ err = cli.limiter.Wait(context.Background())
+ if err != nil {
+ return nil, err
+ }
+ resp, body, errs := request.Clone().Set("Authorization", "Bearer "+cli.token).Get(cli.url + apiPath).End()
+ if errs != nil {
+ err = fmt.Errorf("error calling %s", apiPath)
+ return nil, err
+ }
+ if resp.StatusCode == 200 {
+ err = json.Unmarshal([]byte(body), &response)
+ if err != nil {
+ log.Printf("Error calling func GetSuppressionRules from %s%s, %v ", cli.url, apiPath, err)
+ return nil, err
+ }
+ suppressionRules = append(suppressionRules, response.SuppressionRules...)
+ totalPages = response.Count/response.PageSize + 1
+ page++
+ } else {
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ log.Printf("Failed to read response Body")
+ return nil, err
+ }
+ var errorResponse ErrorResponse
+ err = json.Unmarshal(body, &errorResponse)
+ if err != nil {
+ log.Printf("Failed to Unmarshal response Body to ErrorResponse. Body: %v. error: %v", string(body), err)
+ return nil, err
+ }
+ return nil, fmt.Errorf("failed getting SuppressionRules status: %v. error message: %v", resp.Status, errorResponse.Message)
+ }
+ }
+ return suppressionRules, nil
+}
+
+func (cli *Client) GetSuppressionRule(id string) (*SuppressionRule, error) {
+
+ var err error
+ var response SuppressionRuleResponse
+
+ request := cli.gorequest
+ //request.Set(")
+ apiPath := fmt.Sprintf("/api/v2/images/ack_suppression_rules/search?rule_id=%s", id)
+ err = cli.limiter.Wait(context.Background())
+ if err != nil {
+ return nil, err
+ }
+ resp, body, errs := request.Clone().Set("Authorization", "Bearer "+cli.token).Get(cli.url + apiPath).End()
+ if errs != nil {
+ err = fmt.Errorf("error calling %s", apiPath)
+ return nil, err
+ }
+ if resp.StatusCode == 200 {
+ err = json.Unmarshal([]byte(body), &response)
+ if err != nil {
+ log.Printf("Error calling func GetSuppressionRule from %s%s, %v ", cli.url, apiPath, err)
+ return nil, err
+ }
+ } else {
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ log.Printf("Failed to read response Body")
+ return nil, err
+ }
+ var errorResponse ErrorResponse
+ err = json.Unmarshal(body, &errorResponse)
+ if err != nil {
+ log.Printf("Failed to Unmarshal response Body to ErrorResponse. Body: %v. error: %v", string(body), err)
+ return nil, err
+ }
+ return nil, fmt.Errorf("failed getting SuppressionRule status: %v. error message: %v", resp.Status, errorResponse.Message)
+ }
+ if len(response.SuppressionRules) == 0 {
+ return nil, fmt.Errorf("SuppressionRule not found")
+ }
+ return &response.SuppressionRules[0], nil
+}
+
+func (cli *Client) CreateSuppressionRule(suppressionRule SuppressionRule) (int, error) {
+
+ var err error
+ var response CreateSuppressionRuleResponse
+
+ request := cli.gorequest.Clone()
+ request.Set("Authorization", "Bearer "+cli.token)
+ request.Type("json")
+ request.Send(suppressionRule)
+ apiPath := "/api/v2/images/ack_suppression_rules/add"
+ err = cli.limiter.Wait(context.Background())
+ if err != nil {
+ return 0, err
+ }
+ events, body, errs := request.Clone().Post(cli.url + apiPath).End()
+ if errs != nil {
+ err = fmt.Errorf("error calling %s", apiPath)
+ return 0, err
+ }
+ if events.StatusCode == 200 {
+ err = json.Unmarshal([]byte(body), &response)
+ if err != nil {
+ log.Printf("Error calling func CreateSuppressionRule from %s%s, %v ", cli.url, apiPath, err)
+ return 0, err
+ }
+ }
+ if events.StatusCode != 200 || response.ID == 0 {
+ body, err := io.ReadAll(events.Body)
+ if err != nil {
+ log.Printf("Failed to read response Body")
+ return 0, err
+ }
+ var errorResponse ErrorResponse
+ err = json.Unmarshal(body, &errorResponse)
+ if err != nil {
+ log.Printf("Failed to Unmarshal response Body to ErrorResponse. Body: %v. error: %v", string(body), err)
+ return 0, err
+ }
+ return 0, fmt.Errorf("failed creating SuppressionRule status: %v. error message: %v", events.StatusCode, errorResponse.Message)
+ }
+ return response.ID, nil
+}
+
+func (cli *Client) UpdateSuppressionRule(suppressionRule SuppressionRule) error {
+
+ var err error
+
+ request := cli.gorequest.Clone()
+ request.Set("Authorization", "Bearer "+cli.token)
+ request.Type("json")
+ request.Send(suppressionRule)
+ apiPath := "/api/v2/images/ack_suppression_rules/update"
+ err = cli.limiter.Wait(context.Background())
+ if err != nil {
+ return err
+ }
+ events, _, errs := request.Clone().Put(cli.url + apiPath).End()
+ if errs != nil {
+ err = fmt.Errorf("error calling %s", apiPath)
+ return err
+ }
+ if events.StatusCode != 204 {
+ body, err := io.ReadAll(events.Body)
+ if err != nil {
+ log.Printf("Failed to read response Body")
+ return err
+ }
+ var errorResponse ErrorResponse
+ err = json.Unmarshal(body, &errorResponse)
+ if err != nil {
+ log.Printf("Failed to Unmarshal response Body to ErrorResponse. Body: %v. error: %v", string(body), err)
+ return err
+ }
+ return fmt.Errorf("failed updating SuppressionRule status: %v. error message: %v", events.StatusCode, errorResponse.Message)
+ }
+ return nil
+}
+
+func (cli *Client) DeleteSuppressionRule(id string) error {
+
+ var err error
+
+ request := cli.gorequest.Clone()
+ request.Set("Authorization", "Bearer "+cli.token)
+ request.Send(`[` + id + `]`)
+ apiPath := "/api/v2/images/ack_suppression_rules/delete"
+ err = cli.limiter.Wait(context.Background())
+ if err != nil {
+ return err
+ }
+ events, _, errs := request.Clone().Delete(cli.url + apiPath).End()
+ if errs != nil {
+ err = fmt.Errorf("error calling %s", apiPath)
+ return err
+ }
+ if events.StatusCode != 204 {
+ body, err := io.ReadAll(events.Body)
+ if err != nil {
+ log.Printf("Failed to read response Body")
+ return err
+ }
+ var errorResponse ErrorResponse
+ err = json.Unmarshal(body, &errorResponse)
+ if err != nil {
+ log.Printf("Failed to Unmarshal response Body to ErrorResponse. Body: %v. error: %v", string(body), err)
+ return err
+ }
+ return fmt.Errorf("failed deleting SuppressionRule status: %v. error message: %v", events.StatusCode, errorResponse.Message)
+ }
+ return nil
+}
+
+func (cli *Client) ActivateSuppressionRule(id string) error {
+
+ var err error
+
+ request := cli.gorequest.Clone()
+ request.Set("Authorization", "Bearer "+cli.token)
+ apiPath := fmt.Sprintf("/api/v2/images/ack_suppression_rules/activate/%s", id)
+ err = cli.limiter.Wait(context.Background())
+ if err != nil {
+ return err
+ }
+ events, _, errs := request.Clone().Put(cli.url + apiPath).End()
+ if errs != nil {
+ err = fmt.Errorf("error calling %s", apiPath)
+ return err
+ }
+ if events.StatusCode != 204 {
+ body, err := io.ReadAll(events.Body)
+ if err != nil {
+ log.Printf("Failed to read response Body")
+ return err
+ }
+ var errorResponse ErrorResponse
+ err = json.Unmarshal(body, &errorResponse)
+ if err != nil {
+ log.Printf("Failed to Unmarshal response Body to ErrorResponse. Body: %v. error: %v", string(body), err)
+ return err
+ }
+ return fmt.Errorf("failed activating SuppressionRule status: %v. error message: %v", events.StatusCode, errorResponse.Message)
+ }
+ return nil
+}
+
+func (cli *Client) DisableSuppressionRule(id string) error {
+
+ var err error
+
+ request := cli.gorequest.Clone()
+ request.Set("Authorization", "Bearer "+cli.token)
+ apiPath := fmt.Sprintf("/api/v2/images/ack_suppression_rules/disable/%s", id)
+ err = cli.limiter.Wait(context.Background())
+ if err != nil {
+ return err
+ }
+ events, _, errs := request.Clone().Put(cli.url + apiPath).End()
+ if errs != nil {
+ err = fmt.Errorf("error calling %s", apiPath)
+ return err
+ }
+ if events.StatusCode != 204 {
+ body, err := io.ReadAll(events.Body)
+ if err != nil {
+ log.Printf("Failed to read response Body")
+ return err
+ }
+ var errorResponse ErrorResponse
+ err = json.Unmarshal(body, &errorResponse)
+ if err != nil {
+ log.Printf("Failed to Unmarshal response Body to ErrorResponse. Body: %v. error: %v", string(body), err)
+ return err
+ }
+ return fmt.Errorf("failed disabling SuppressionRule status: %v. error message: %v", events.StatusCode, errorResponse.Message)
+ }
+ return nil
+}
diff --git a/docs/data-sources/suppression_rules.md b/docs/data-sources/suppression_rules.md
new file mode 100644
index 00000000..1e983dc1
--- /dev/null
+++ b/docs/data-sources/suppression_rules.md
@@ -0,0 +1,67 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "aquasec_suppression_rules Data Source - terraform-provider-aquasec"
+subcategory: ""
+description: |-
+ Fetches the list of suppression rules.
+---
+
+# aquasec_suppression_rules (Data Source)
+
+Fetches the list of suppression rules.
+
+## Example Usage
+
+```terraform
+# List all suppression rules.
+data "aquasec_suppression_rules" "suppression_rules" {}
+
+output "suppression_rules" {
+ value = { for rule in data.aquasec_suppression_rules.suppression_rules.suppression_rules : rule.id => rule.name }
+}
+```
+
+
+## Schema
+
+### Read-Only
+
+- `suppression_rules` (Attributes List) (see [below for nested schema](#nestedatt--suppression_rules))
+
+
+### Nested Schema for `suppression_rules`
+
+Read-Only:
+
+- `application_scopes` (List of String) List of application scopes for the suppression rule.
+- `author` (String) Author of the suppression rule.
+- `comment` (String) Comment for the suppression rule.
+- `created` (String) Creation date of the suppression rule.
+- `expiry` (Number) Expiry in days of the suppression rule.
+- `fix_available` (String) Fix available for the suppression rule.
+- `id` (Number) Identifier used by AquaSec to identify the suppression rule.
+- `name` (String) Name of the suppression rule.
+- `scope` (Attributes) Scope of the suppression rule. (see [below for nested schema](#nestedatt--suppression_rules--scope))
+- `score` (List of Number) List of scores for the suppression rule.
+- `severity` (String) Severity of the suppression rule.
+- `status` (Boolean) Status of the suppression rule.
+- `vulnerabilities` (String) Vulnerabilities as comma separated list for the suppression rule.
+
+
+### Nested Schema for `suppression_rules.scope`
+
+Read-Only:
+
+- `expression` (String) Expression of the suppression rule.
+- `variables` (Attributes List) Variables of the suppression rule. (see [below for nested schema](#nestedatt--suppression_rules--scope--variables))
+
+
+### Nested Schema for `suppression_rules.scope.variables`
+
+Read-Only:
+
+- `attribute` (String) Attribute of the variable.
+- `name` (String) Name of the variable.
+- `value` (String) Value of the variable.
+
+
diff --git a/docs/resources/suppression_rule.md b/docs/resources/suppression_rule.md
new file mode 100644
index 00000000..49223f5c
--- /dev/null
+++ b/docs/resources/suppression_rule.md
@@ -0,0 +1,78 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "aquasec_suppression_rule Resource - terraform-provider-aquasec"
+subcategory: ""
+description: |-
+ Manages a suppression rule.
+---
+
+# aquasec_suppression_rule (Resource)
+
+Manages a suppression rule.
+
+## Example Usage
+
+```terraform
+# Manage example suppression rule
+resource "aquasec_suppression_rule" "example" {
+ name = "Example Suppression Rule"
+ application_scopes = ["Global"]
+ score = []
+ fix_available = false
+ comment = "This is an example suppression rule"
+}
+```
+
+
+## Schema
+
+### Required
+
+- `application_scopes` (List of String) List of application scopes for the suppression rule.
+- `comment` (String) Comment for the suppression rule.
+- `name` (String) Name of the suppression rule.
+
+### Optional
+
+- `expiry` (Number) Expiry in days of the suppression rule.
+- `fix_available` (String) Fix available for the suppression rule.
+- `scope` (Attributes) Scope of the suppression rule. (see [below for nested schema](#nestedatt--scope))
+- `score` (List of Number) List of scores for the suppression rule.
+- `severity` (String) Severity of the suppression rule.
+- `status` (Boolean) Activation status of the suppression rule.
+- `vulnerabilities` (String) Vulnerabilities for the suppression rule.
+
+### Read-Only
+
+- `author` (String) Author of the suppression rule.
+- `created` (String) Creation date of the suppression rule.
+- `id` (String) Numeric identifier of the suppression rule.
+
+
+### Nested Schema for `scope`
+
+Optional:
+
+- `expression` (String) Expression of the suppression rule.
+- `variables` (Attributes List) (see [below for nested schema](#nestedatt--scope--variables))
+
+
+### Nested Schema for `scope.variables`
+
+Required:
+
+- `attribute` (String) Attribute of the variable.
+- `value` (String) Value of the variable.
+
+Optional:
+
+- `name` (String) Name of the variable.
+
+## Import
+
+Import is supported using the following syntax:
+
+```shell
+# Suppression rule can be imported by specifying the numeric identifier.
+terraform import aquasec_suppression_rule.example 123
+```
diff --git a/examples/data-sources/aquasec_suppression_rules/data-source.tf b/examples/data-sources/aquasec_suppression_rules/data-source.tf
new file mode 100644
index 00000000..26a7b67b
--- /dev/null
+++ b/examples/data-sources/aquasec_suppression_rules/data-source.tf
@@ -0,0 +1,6 @@
+# List all suppression rules.
+data "aquasec_suppression_rules" "suppression_rules" {}
+
+output "suppression_rules" {
+ value = { for rule in data.aquasec_suppression_rules.suppression_rules.suppression_rules : rule.id => rule.name }
+}
diff --git a/examples/resources/aquasec_suppression_rule/import.sh b/examples/resources/aquasec_suppression_rule/import.sh
new file mode 100644
index 00000000..4447599e
--- /dev/null
+++ b/examples/resources/aquasec_suppression_rule/import.sh
@@ -0,0 +1,2 @@
+# Suppression rule can be imported by specifying the numeric identifier.
+terraform import aquasec_suppression_rule.example 123
diff --git a/examples/resources/aquasec_suppression_rule/resource.tf b/examples/resources/aquasec_suppression_rule/resource.tf
new file mode 100644
index 00000000..f9eec0de
--- /dev/null
+++ b/examples/resources/aquasec_suppression_rule/resource.tf
@@ -0,0 +1,8 @@
+# Manage example suppression rule
+resource "aquasec_suppression_rule" "example" {
+ name = "Example Suppression Rule"
+ application_scopes = ["Global"]
+ score = []
+ fix_available = false
+ comment = "This is an example suppression rule"
+}
diff --git a/go.mod b/go.mod
index b7312108..59e6313d 100644
--- a/go.mod
+++ b/go.mod
@@ -1,55 +1,61 @@
module github.com/aquasecurity/terraform-provider-aquasec
-go 1.18
+go 1.21
+
+toolchain go1.21.4
require (
github.com/hashicorp/terraform-plugin-docs v0.13.0
- github.com/hashicorp/terraform-plugin-sdk/v2 v2.19.0
+ github.com/hashicorp/terraform-plugin-framework v1.11.0
+ github.com/hashicorp/terraform-plugin-go v0.23.0
+ github.com/hashicorp/terraform-plugin-log v0.9.0
+ github.com/hashicorp/terraform-plugin-mux v0.16.0
+ github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0
+ github.com/hashicorp/terraform-plugin-testing v1.10.0
github.com/mitchellh/go-homedir v1.1.0
github.com/parnurzeal/gorequest v0.2.16
github.com/pkg/errors v0.9.1
- golang.org/x/net v0.23.0
+ golang.org/x/net v0.25.0
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9
)
require (
github.com/Masterminds/goutils v1.1.1 // indirect
- github.com/Masterminds/semver/v3 v3.1.1 // indirect
- github.com/Masterminds/sprig/v3 v3.2.2 // indirect
+ github.com/Masterminds/semver/v3 v3.2.0 // indirect
+ github.com/Masterminds/sprig/v3 v3.2.3 // indirect
+ github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect
github.com/agext/levenshtein v1.2.2 // indirect
- github.com/apparentlymart/go-cidr v1.1.0 // indirect
- github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
+ github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
- github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/cloudflare/circl v1.3.7 // indirect
github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e // indirect
- github.com/fatih/color v1.13.0 // indirect
- github.com/golang/protobuf v1.5.3 // indirect
- github.com/google/go-cmp v0.5.9 // indirect
- github.com/google/uuid v1.3.0 // indirect
+ github.com/fatih/color v1.16.0 // indirect
+ github.com/golang/protobuf v1.5.4 // indirect
+ github.com/google/go-cmp v0.6.0 // indirect
+ github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-checkpoint v0.5.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect
- github.com/hashicorp/go-hclog v1.2.1 // indirect
+ github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
- github.com/hashicorp/go-plugin v1.4.4 // indirect
+ github.com/hashicorp/go-plugin v1.6.0 // indirect
+ github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
- github.com/hashicorp/go-version v1.6.0 // indirect
- github.com/hashicorp/hc-install v0.4.0 // indirect
- github.com/hashicorp/hcl/v2 v2.13.0 // indirect
+ github.com/hashicorp/go-version v1.7.0 // indirect
+ github.com/hashicorp/hc-install v0.8.0 // indirect
+ github.com/hashicorp/hcl/v2 v2.21.0 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect
- github.com/hashicorp/terraform-exec v0.17.2 // indirect
- github.com/hashicorp/terraform-json v0.14.0 // indirect
- github.com/hashicorp/terraform-plugin-go v0.12.0 // indirect
- github.com/hashicorp/terraform-plugin-log v0.6.0 // indirect
- github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c // indirect
- github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect
- github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect
- github.com/huandu/xstrings v1.3.2 // indirect
- github.com/imdario/mergo v0.3.13 // indirect
- github.com/mattn/go-colorable v0.1.12 // indirect
- github.com/mattn/go-isatty v0.0.14 // indirect
+ github.com/hashicorp/terraform-exec v0.21.0 // indirect
+ github.com/hashicorp/terraform-json v0.22.1 // indirect
+ github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
+ github.com/hashicorp/terraform-svchost v0.1.1 // indirect
+ github.com/hashicorp/yamux v0.1.1 // indirect
+ github.com/huandu/xstrings v1.3.3 // indirect
+ github.com/imdario/mergo v0.3.15 // indirect
+ github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/cli v1.1.4 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
@@ -63,15 +69,18 @@ require (
github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
- github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect
- github.com/vmihailenco/tagparser v0.1.1 // indirect
- github.com/zclconf/go-cty v1.10.0 // indirect
- golang.org/x/crypto v0.21.0 // indirect
- golang.org/x/sys v0.18.0 // indirect
- golang.org/x/text v0.14.0 // indirect
- google.golang.org/appengine v1.6.7 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
- google.golang.org/grpc v1.56.3 // indirect
- google.golang.org/protobuf v1.33.0 // indirect
+ github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
+ github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
+ github.com/zclconf/go-cty v1.15.0 // indirect
+ golang.org/x/crypto v0.26.0 // indirect
+ golang.org/x/mod v0.19.0 // indirect
+ golang.org/x/sync v0.8.0 // indirect
+ golang.org/x/sys v0.23.0 // indirect
+ golang.org/x/text v0.17.0 // indirect
+ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
+ google.golang.org/appengine v1.6.8 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
+ google.golang.org/grpc v1.63.2 // indirect
+ google.golang.org/protobuf v1.34.0 // indirect
moul.io/http2curl v1.0.0 // indirect
)
diff --git a/go.sum b/go.sum
index 38337ea9..68c3bfe6 100644
--- a/go.sum
+++ b/go.sum
@@ -1,76 +1,71 @@
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
+dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
-github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
+github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
+github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI=
-github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
-github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
-github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
-github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
-github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
-github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
-github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
-github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
-github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
+github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
+github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
+github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
+github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
+github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg=
+github.com/ProtonMail/go-crypto v1.1.0-alpha.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE=
github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
-github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU=
-github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
-github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFUye+ZcSR6opIgz9Co7WcDx6ZcY+RjfFHoA0I=
-github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
-github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
-github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
+github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
+github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
+github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
+github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
+github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
+github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
+github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e h1:/cwV7t2xezilMljIftb7WlFtzGANRCnoOhPjtl2ifcs=
github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
-github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
-github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
+github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
+github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
-github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
+github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
+github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
-github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
-github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
-github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
-github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
-github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
-github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
-github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
-github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
-github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
+github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
+github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
+github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
+github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
+github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
+github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
+github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
+github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
-github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
-github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -79,83 +74,87 @@ github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv
github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU=
github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
-github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI=
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs=
-github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw=
-github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
+github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
+github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
-github.com/hashicorp/go-plugin v1.4.4 h1:NVdrSdFRt3SkZtNckJ6tog7gbpRrcbOjQi/rgF7JYWQ=
-github.com/hashicorp/go-plugin v1.4.4/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
+github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A=
+github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI=
+github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
+github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
-github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
-github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
-github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
-github.com/hashicorp/hc-install v0.4.0 h1:cZkRFr1WVa0Ty6x5fTvL1TuO1flul231rWkGH92oYYk=
-github.com/hashicorp/hc-install v0.4.0/go.mod h1:5d155H8EC5ewegao9A4PUTMNPZaq+TbOzkJJZ4vrXeI=
-github.com/hashicorp/hcl/v2 v2.13.0 h1:0Apadu1w6M11dyGFxWnmhhcMjkbAiKCv7G1r/2QgCNc=
-github.com/hashicorp/hcl/v2 v2.13.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0=
+github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
+github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/hc-install v0.8.0 h1:LdpZeXkZYMQhoKPCecJHlKvUkQFixN/nvyR1CdfOLjI=
+github.com/hashicorp/hc-install v0.8.0/go.mod h1:+MwJYjDfCruSD/udvBmRB22Nlkwwkwf5sAB6uTIhSaU=
+github.com/hashicorp/hcl/v2 v2.21.0 h1:lve4q/o/2rqwYOgUg3y3V2YPyD1/zkCLGjIV74Jit14=
+github.com/hashicorp/hcl/v2 v2.21.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
-github.com/hashicorp/terraform-exec v0.17.2 h1:EU7i3Fh7vDUI9nNRdMATCEfnm9axzTnad8zszYZ73Go=
-github.com/hashicorp/terraform-exec v0.17.2/go.mod h1:tuIbsL2l4MlwwIZx9HPM+LOV9vVyEfBYu2GsO1uH3/8=
-github.com/hashicorp/terraform-json v0.14.0 h1:sh9iZ1Y8IFJLx+xQiKHGud6/TSUCM0N8e17dKDpqV7s=
-github.com/hashicorp/terraform-json v0.14.0/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM=
+github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ=
+github.com/hashicorp/terraform-exec v0.21.0/go.mod h1:1PPeMYou+KDUSSeRE9szMZ/oHf4fYUmB923Wzbq1ICg=
+github.com/hashicorp/terraform-json v0.22.1 h1:xft84GZR0QzjPVWs4lRUwvTcPnegqlyS7orfb5Ltvec=
+github.com/hashicorp/terraform-json v0.22.1/go.mod h1:JbWSQCLFSXFFhg42T7l9iJwdGXBYV8fmmD6o/ML4p3A=
github.com/hashicorp/terraform-plugin-docs v0.13.0 h1:6e+VIWsVGb6jYJewfzq2ok2smPzZrt1Wlm9koLeKazY=
github.com/hashicorp/terraform-plugin-docs v0.13.0/go.mod h1:W0oCmHAjIlTHBbvtppWHe8fLfZ2BznQbuv8+UD8OucQ=
-github.com/hashicorp/terraform-plugin-go v0.12.0 h1:6wW9mT1dSs0Xq4LR6HXj1heQ5ovr5GxXNJwkErZzpJw=
-github.com/hashicorp/terraform-plugin-go v0.12.0/go.mod h1:kwhmaWHNDvT1B3QiSJdAtrB/D4RaKSY/v3r2BuoWK4M=
-github.com/hashicorp/terraform-plugin-log v0.6.0 h1:/Vq78uSIdUSZ3iqDc9PESKtwt8YqNKN6u+khD+lLjuw=
-github.com/hashicorp/terraform-plugin-log v0.6.0/go.mod h1:p4R1jWBXRTvL4odmEkFfDdhUjHf9zcs/BCoNHAc7IK4=
-github.com/hashicorp/terraform-plugin-sdk/v2 v2.19.0 h1:7gDAcfto/C4Cjtf90SdukQshsxdMxJ/P69QxiF3digI=
-github.com/hashicorp/terraform-plugin-sdk/v2 v2.19.0/go.mod h1:/WYikYjhKB7c2j1HmXZhRsAARldRb4M38bLCLOhC3so=
-github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c h1:D8aRO6+mTqHfLsK/BC3j5OAoogv1WLRWzY1AaTo3rBg=
-github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c/go.mod h1:Wn3Na71knbXc1G8Lh+yu/dQWWJeFQEpDeJMtWMtlmNI=
-github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKLsbzeOsfXmKNpr3GiT18XAblV0BjCbzL8KQAMZGa0=
-github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg=
-github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
-github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
+github.com/hashicorp/terraform-plugin-framework v1.11.0 h1:M7+9zBArexHFXDx/pKTxjE6n/2UCXY6b8FIq9ZYhwfE=
+github.com/hashicorp/terraform-plugin-framework v1.11.0/go.mod h1:qBXLDn69kM97NNVi/MQ9qgd1uWWsVftGSnygYG1tImM=
+github.com/hashicorp/terraform-plugin-go v0.23.0 h1:AALVuU1gD1kPb48aPQUjug9Ir/125t+AAurhqphJ2Co=
+github.com/hashicorp/terraform-plugin-go v0.23.0/go.mod h1:1E3Cr9h2vMlahWMbsSEcNrOCxovCZhOOIXjFHbjc/lQ=
+github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=
+github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow=
+github.com/hashicorp/terraform-plugin-mux v0.16.0 h1:RCzXHGDYwUwwqfYYWJKBFaS3fQsWn/ZECEiW7p2023I=
+github.com/hashicorp/terraform-plugin-mux v0.16.0/go.mod h1:PF79mAsPc8CpusXPfEVa4X8PtkB+ngWoiUClMrNZlYo=
+github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 h1:kJiWGx2kiQVo97Y5IOGR4EMcZ8DtMswHhUuFibsCQQE=
+github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0/go.mod h1:sl/UoabMc37HA6ICVMmGO+/0wofkVIRxf+BMb/dnoIg=
+github.com/hashicorp/terraform-plugin-testing v1.10.0 h1:2+tmRNhvnfE4Bs8rB6v58S/VpqzGC6RCh9Y8ujdn+aw=
+github.com/hashicorp/terraform-plugin-testing v1.10.0/go.mod h1:iWRW3+loP33WMch2P/TEyCxxct/ZEcCGMquSLSCVsrc=
+github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI=
+github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM=
+github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=
+github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc=
+github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
+github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
-github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
+github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
-github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
-github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
-github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
+github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
+github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
-github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
-github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
+github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
+github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
-github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
-github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
+github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
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 v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
-github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
-github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/cli v1.1.4 h1:qj8czE26AU4PbiaPXK5uVmMSM+V5BYsFBiM9HhGRLUA=
github.com/mitchellh/cli v1.1.4/go.mod h1:vTLESy5mRhKOs9KDp0/RATawxP1UqBmdrpVRMnpcvKQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
@@ -172,13 +171,12 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k+Mg7cowZ8yv4Trqw9UsJby758=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/parnurzeal/gorequest v0.2.16 h1:T/5x+/4BT+nj+3eSknXmCTnEVGSzFzPGdpqmUVVZXHQ=
github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
+github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -188,15 +186,16 @@ github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXq
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
-github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4=
-github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
-github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
+github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
+github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
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/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
+github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
+github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
@@ -205,111 +204,107 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
-github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U=
-github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
-github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
-github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
-github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
-github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
-github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
-github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
-github.com/zclconf/go-cty v1.10.0 h1:mp9ZXQeIcN8kAwuqorjH+Q+njbJKjLrvB2yIh4q7U+0=
-github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
-github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
-golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
+github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
+github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
+github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
+github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
+github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ=
+github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
+github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
+github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
-golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
+golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
+golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
+golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
-golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
-golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
+golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
+golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
+golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
+golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
+golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
+golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ=
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
-google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
-google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
-google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
+google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
+google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
+google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
+google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
-google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4=
+google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8=
diff --git a/internal/provider/provider.go b/internal/provider/provider.go
new file mode 100644
index 00000000..3f02c234
--- /dev/null
+++ b/internal/provider/provider.go
@@ -0,0 +1,356 @@
+package provider
+
+import (
+ "context"
+ "encoding/json"
+ "io"
+ "os"
+
+ sdkProvider "github.com/aquasecurity/terraform-provider-aquasec/aquasec"
+ aquasec "github.com/aquasecurity/terraform-provider-aquasec/client"
+
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/provider"
+ "github.com/hashicorp/terraform-plugin-framework/provider/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+ "github.com/mitchellh/go-homedir"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+ _ provider.Provider = &aquasecProvider{}
+)
+
+// New is a helper function to simplify provider server and testing implementation.
+func New(version string) func() provider.Provider {
+ return func() provider.Provider {
+ return &aquasecProvider{
+ version: version,
+ }
+ }
+}
+
+// aquasecProvider is the provider implementation.
+type aquasecProvider struct {
+ // version is set to the provider version on release, "dev" when the
+ // provider is built and ran locally, and "test" when running acceptance
+ // testing.
+ version string
+}
+
+// aquasecProviderModel maps provider schema data to a Go type.
+type aquasecProviderModel struct {
+ Username types.String `tfsdk:"username"`
+ Password types.String `tfsdk:"password"`
+ AquaURL types.String `tfsdk:"aqua_url"`
+ VerifyTLS types.Bool `tfsdk:"verify_tls"`
+ CACertificatePath types.String `tfsdk:"ca_certificate_path"`
+ ConfigPath types.String `tfsdk:"config_path"`
+}
+
+// Metadata returns the provider type name.
+func (p *aquasecProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
+ resp.TypeName = "aquasec"
+ resp.Version = p.version
+}
+
+// Schema defines the provider-level schema for configuration data.
+func (p *aquasecProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ Attributes: map[string]schema.Attribute{
+ "username": schema.StringAttribute{
+ Optional: true,
+ Sensitive: true,
+ Description: "This is the user id that should be used to make the connection. Can alternatively be sourced from the `AQUA_USER` environment variable.",
+ },
+ "password": schema.StringAttribute{
+ Optional: true,
+ Sensitive: true,
+ Description: "This is the password that should be used to make the connection. Can alternatively be sourced from the `AQUA_PASSWORD` environment variable.",
+ },
+ "aqua_url": schema.StringAttribute{
+ Optional: true,
+ Description: "This is the base URL of your Aqua instance. Can alternatively be sourced from the `AQUA_URL` environment variable.",
+ },
+ "verify_tls": schema.BoolAttribute{
+ Optional: true,
+ Description: "If true, server tls certificates will be verified by the client before making a connection. Defaults to true. Can alternatively be sourced from the `AQUA_TLS_VERIFY` environment variable.",
+ },
+ "ca_certificate_path": schema.StringAttribute{
+ Optional: true,
+ Description: "This is the file path for server CA certificates if they are not available on the host OS. Can alternatively be sourced from the `AQUA_CA_CERT_PATH` environment variable.",
+ },
+ "config_path": schema.StringAttribute{
+ Optional: true,
+ Description: "This is the file path for Aqua provider configuration. The default configuration path is `~/.aqua/tf.config`. Can alternatively be sourced from the `AQUA_CONFIG` environment variable.",
+ },
+ },
+ }
+}
+
+// Configure prepares a AquaSec API client for data sources and resources.
+func (p *aquasecProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
+ tflog.Info(ctx, "Configuring AquaSec client")
+
+ // Retrieve provider data from configuration
+ var config aquasecProviderModel
+ diags := req.Config.Get(ctx, &config)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // If practitioner provided a configuration value for any of the
+ // attributes, it must be a known value.
+
+ if config.Username.IsUnknown() {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("username"),
+ "Unknown AquaSec API Username",
+ "The provider cannot create the AquaSec API client as there is an unknown configuration value for the AquaSec API username. "+
+ "Either target apply the source of the value first, set the value statically in the configuration, or use the AQUA_USER environment variable.",
+ )
+ }
+
+ if config.Password.IsUnknown() {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("password"),
+ "Unknown AquaSec API Password",
+ "The provider cannot create the AquaSec API client as there is an unknown configuration value for the AquaSec API password. "+
+ "Either target apply the source of the value first, set the value statically in the configuration, or use the AQUA_PASSWORD environment variable.",
+ )
+ }
+
+ if config.AquaURL.IsUnknown() {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("aqua_url"),
+ "Unknown AquaSec API URL",
+ "The provider cannot create the AquaSec API client as there is an unknown configuration value for the AquaSec API URL. "+
+ "Either target apply the source of the value first, set the value statically in the configuration, or use the AQUA_URL environment variable.",
+ )
+ }
+
+ if config.VerifyTLS.IsUnknown() {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("verify_tls"),
+ "Unknown AquaSec API TLS Verification",
+ "The provider cannot create the AquaSec API client as there is an unknown configuration value for the AquaSec API TLS verification. "+
+ "Either target apply the source of the value first, set the value statically in the configuration, or use the AQUA_TLS_VERIFY environment variable.",
+ )
+ }
+
+ if config.CACertificatePath.IsUnknown() {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("ca_certificate_path"),
+ "Unknown AquaSec API CA Certificate Path",
+ "The provider cannot create the AquaSec API client as there is an unknown configuration value for the AquaSec API CA certificate path. "+
+ "Either target apply the source of the value first, set the value statically in the configuration, or use the AQUA_CA_CERT_PATH environment variable.",
+ )
+ }
+
+ if config.ConfigPath.IsUnknown() {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("config_path"),
+ "Unknown AquaSec API Configuration Path",
+ "The provider cannot create the AquaSec API client as there is an unknown configuration value for the AquaSec API configuration path. "+
+ "Either target apply the source of the value first, set the value statically in the configuration, or use the AQUA_CONFIG environment variable.",
+ )
+ }
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Default values to environment variables, but override
+ // with Terraform configuration value if set.
+
+ username := os.Getenv("AQUA_USER")
+ password := os.Getenv("AQUA_PASSWORD")
+ aquaURL := os.Getenv("AQUA_URL")
+ verifyTLS := true
+ if os.Getenv("AQUA_TLS_VERIFY") == "false" {
+ verifyTLS = false
+ }
+ caCertificatePath := os.Getenv("AQUA_CA_CERT_PATH")
+ configPath := "~/.aquasec/tf.config"
+ if os.Getenv("AQUA_CONFIG") != "" {
+ configPath = os.Getenv("AQUA_CONFIG")
+ }
+
+ if !config.Username.IsNull() {
+ username = config.Username.ValueString()
+ }
+
+ if !config.Password.IsNull() {
+ password = config.Password.ValueString()
+ }
+
+ if !config.AquaURL.IsNull() {
+ aquaURL = config.AquaURL.ValueString()
+ }
+
+ if !config.VerifyTLS.IsNull() {
+ verifyTLS = config.VerifyTLS.ValueBool()
+ }
+
+ if !config.CACertificatePath.IsNull() {
+ caCertificatePath = config.CACertificatePath.ValueString()
+ }
+
+ if !config.ConfigPath.IsNull() {
+ configPath = config.ConfigPath.ValueString()
+ }
+
+ if username == "" && password == "" && aquaURL == "" {
+ // If no configuration values are set, check the AquaSec provider
+ // configuration file for values.
+ configPath, err := homedir.Expand(configPath)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to Expand AquaSec Configuration Path",
+ "An unexpected error occurred when expanding the AquaSec configuration path. "+
+ "If the error is not clear, please contact the provider developers.\n\n"+
+ "Configuration Path Error: "+err.Error(),
+ )
+ return
+ }
+
+ // Read the AquaSec provider configuration file.
+ file, err := os.Open(configPath)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to Open AquaSec Configuration File",
+ "An unexpected error occurred when opening the AquaSec configuration file. "+
+ "If the error is not clear, please contact the provider developers.\n\n"+
+ "Configuration File Error: "+err.Error(),
+ )
+ return
+ }
+ defer file.Close()
+
+ // Read the AquaSec provider configuration file contents.
+ configBytes, err := io.ReadAll(file)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to Read AquaSec Configuration File",
+ "An unexpected error occurred when reading the AquaSec configuration file. "+
+ "If the error is not clear, please contact the provider developers.\n\n"+
+ "Configuration File Error: "+err.Error(),
+ )
+ return
+ }
+
+ // Unmarshal the AquaSec provider configuration file contents.
+ var config sdkProvider.Config
+ err = json.Unmarshal(configBytes, &config)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to Parse AquaSec Configuration File",
+ "An unexpected error occurred when parsing the AquaSec configuration file. "+
+ "If the error is not clear, please contact the provider developers.\n\n"+
+ "Configuration File Error: "+err.Error(),
+ )
+ return
+ }
+
+ // Set the AquaSec provider configuration values.
+ username = config.Username
+ password = config.Password
+ aquaURL = config.AquaURL
+ }
+
+ // If any of the expected configurations are missing, return
+ // errors with provider-specific guidance.
+
+ if username == "" {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("username"),
+ "Missing AquaSec API Username",
+ "The provider cannot create the AquaSec API client as there is a missing or empty value for the AquaSec API username. "+
+ "Set the username value in the configuration or use the AQUA_USER environment variable. "+
+ "If either is already set, ensure the value is not empty.",
+ )
+ }
+
+ if password == "" {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("password"),
+ "Missing AquaSec API Password",
+ "The provider cannot create the AquaSec API client as there is a missing or empty value for the AquaSec API password. "+
+ "Set the password value in the configuration or use the AQUA_PASSWORD environment variable. "+
+ "If either is already set, ensure the value is not empty.",
+ )
+ }
+
+ if aquaURL == "" {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("aqua_url"),
+ "Missing AquaSec API URL",
+ "The provider cannot create the AquaSec API client as there is a missing or empty value for the AquaSec API URL. "+
+ "Set the URL value in the configuration or use the AQUA_URL environment variable. "+
+ "If either is already set, ensure the value is not empty.",
+ )
+ }
+
+ var caCertByte []byte
+ if caCertificatePath != "" {
+ var err error
+ caCertByte, err = os.ReadFile(caCertificatePath)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to read CA certificates",
+ "An unexpected error occurred when reading the CA certificates file. "+
+ "If the error is not clear, please contact the provider developers.\n\n"+
+ "CA Certificates Error: "+err.Error(),
+ )
+ return
+ }
+ }
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ ctx = tflog.SetField(ctx, "aqua_url", aquaURL)
+ ctx = tflog.SetField(ctx, "aqua_user", username)
+ ctx = tflog.SetField(ctx, "aqua_password", password)
+ ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "aqua_password")
+
+ tflog.Debug(ctx, "Creating AquaSec client")
+
+ // Create a new AquaSec client using the configuration values
+ client := aquasec.NewClient(aquaURL, username, password, verifyTLS, caCertByte)
+ _, _, err := client.GetAuthToken()
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to Create AquaSec API Client",
+ "An unexpected error occurred when creating the AquaSec API client. "+
+ "If the error is not clear, please contact the provider developers.\n\n"+
+ "AquaSec Client Error: "+err.Error(),
+ )
+ }
+
+ // Make the AquaSec client available during DataSource and Resource
+ // type Configure methods.
+ resp.DataSourceData = client
+ resp.ResourceData = client
+
+ tflog.Info(ctx, "Configured AquaSec client", map[string]any{"success": true})
+}
+
+// DataSources defines the data sources implemented in the provider.
+func (p *aquasecProvider) DataSources(_ context.Context) []func() datasource.DataSource {
+ return []func() datasource.DataSource{
+ NewSuppressionRulesDataSource,
+ }
+}
+
+// Resources defines the resources implemented in the provider.
+func (p *aquasecProvider) Resources(_ context.Context) []func() resource.Resource {
+ return []func() resource.Resource{
+ NewSuppressionRuleResource,
+ }
+}
diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go
new file mode 100644
index 00000000..f4e048fa
--- /dev/null
+++ b/internal/provider/provider_test.go
@@ -0,0 +1,75 @@
+package provider
+
+import (
+ "context"
+ "testing"
+
+ "github.com/aquasecurity/terraform-provider-aquasec/aquasec"
+ "github.com/hashicorp/terraform-plugin-framework/providerserver"
+ "github.com/hashicorp/terraform-plugin-go/tfprotov6"
+ "github.com/hashicorp/terraform-plugin-mux/tf5to6server"
+ "github.com/hashicorp/terraform-plugin-mux/tf6muxserver"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+var version string
+
+func TestMuxServer(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){
+ "aquasec": func() (tfprotov6.ProviderServer, error) {
+ ctx := context.Background()
+
+ upgradedSdkServer, err := tf5to6server.UpgradeServer(
+ ctx,
+ aquasec.Provider(version).GRPCProvider,
+ )
+
+ if err != nil {
+ return nil, err
+ }
+
+ providers := []func() tfprotov6.ProviderServer{
+ providerserver.NewProtocol6(New(version)()),
+ func() tfprotov6.ProviderServer {
+ return upgradedSdkServer
+ },
+ }
+
+ muxServer, err := tf6muxserver.NewMuxServer(ctx, providers...)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return muxServer.ProviderServer(), nil
+ },
+ },
+ Steps: []resource.TestStep{
+ {
+ Config: providerConfig + `data "aquasec_suppression_rules" "test" {}`,
+ },
+ },
+ })
+}
+
+const (
+ // providerConfig is a shared configuration to combine with the actual
+ // test configuration so the AquaSec client is properly configured.
+ // It is also possible to use the AQUASEC_ environment variables instead,
+ // such as updating the Makefile and running the testing through that tool.
+ providerConfig = `
+provider "aquasec" {
+}
+`
+)
+
+var (
+ // testAccProtoV6ProviderFactories are used to instantiate a provider during
+ // acceptance testing. The factory function will be invoked for every Terraform
+ // CLI command executed to create a provider server to which the CLI can
+ // reattach.
+ testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
+ "aquasec": providerserver.NewProtocol6WithError(New("test")()),
+ }
+)
diff --git a/internal/provider/suppression_rule_resource.go b/internal/provider/suppression_rule_resource.go
new file mode 100644
index 00000000..bfd2e42a
--- /dev/null
+++ b/internal/provider/suppression_rule_resource.go
@@ -0,0 +1,575 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+ "strconv"
+
+ aquasec "github.com/aquasecurity/terraform-provider-aquasec/client"
+
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/listdefault"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectdefault"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+ _ resource.Resource = &suppressionRuleResource{}
+ _ resource.ResourceWithConfigure = &suppressionRuleResource{}
+ _ resource.ResourceWithImportState = &suppressionRuleResource{}
+)
+
+// NewSuppressionRuleResource is a helper function to simplify the provider implementation.
+func NewSuppressionRuleResource() resource.Resource {
+ return &suppressionRuleResource{}
+}
+
+// suppressionRuleResource is the resource implementation.
+type suppressionRuleResource struct {
+ client *aquasec.Client
+}
+
+// suppressionRuleResourceModel maps the resource schema data.
+type suppressionRuleResourceModel struct {
+ ID types.String `tfsdk:"id"`
+ Name types.String `tfsdk:"name"`
+ ApplicationScopes []types.String `tfsdk:"application_scopes"`
+ Scope *suppressionRuleScopeModel `tfsdk:"scope"`
+ Score []types.Int64 `tfsdk:"score"`
+ Severity types.String `tfsdk:"severity"`
+ FixAvailable types.String `tfsdk:"fix_available"`
+ Vulnerabilities types.String `tfsdk:"vulnerabilities"`
+ Expiry types.Int64 `tfsdk:"expiry"`
+ Comment types.String `tfsdk:"comment"`
+ Created types.String `tfsdk:"created"`
+ Author types.String `tfsdk:"author"`
+ Status types.Bool `tfsdk:"status"`
+}
+
+// suppressionRuleScopeModel maps suppression rule scope data.
+type suppressionRuleScopeModel struct {
+ Expression types.String `tfsdk:"expression"`
+ Variables []suppressionRuleVariableModel `tfsdk:"variables"`
+}
+
+// suppressionRuleVariableModel maps suppression rule variable data.
+type suppressionRuleVariableModel struct {
+ Attribute types.String `tfsdk:"attribute"`
+ Value types.String `tfsdk:"value"`
+ Name types.String `tfsdk:"name"`
+}
+
+// Metadata returns the resource type name.
+func (r *suppressionRuleResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_suppression_rule"
+}
+
+// Schema defines the schema for the resource.
+func (r *suppressionRuleResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ Attributes: map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ Computed: true,
+ Description: "Numeric identifier of the suppression rule.",
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "name": schema.StringAttribute{
+ Required: true,
+ Description: "Name of the suppression rule.",
+ },
+ "application_scopes": schema.ListAttribute{
+ ElementType: types.StringType,
+ Required: true,
+ Description: "List of application scopes for the suppression rule.",
+ },
+ "scope": schema.SingleNestedAttribute{
+ Attributes: map[string]schema.Attribute{
+ "expression": schema.StringAttribute{
+ Optional: true,
+ Description: "Expression of the suppression rule.",
+ },
+ "variables": schema.ListNestedAttribute{
+ Optional: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "attribute": schema.StringAttribute{
+ Required: true,
+ Description: "Attribute of the variable.",
+ },
+ "value": schema.StringAttribute{
+ Required: true,
+ Description: "Value of the variable.",
+ },
+ "name": schema.StringAttribute{
+ Optional: true,
+ Description: "Name of the variable.",
+ },
+ },
+ },
+ },
+ },
+ Optional: true,
+ Computed: true,
+ Description: "Scope of the suppression rule.",
+ PlanModifiers: []planmodifier.Object{
+ objectplanmodifier.RequiresReplace(),
+ },
+ Default: objectdefault.StaticValue(types.ObjectValueMust(
+ map[string]attr.Type{
+ "expression": types.StringType,
+ "variables": types.ListType{
+ ElemType: types.ObjectType{
+ AttrTypes: map[string]attr.Type{
+ "attribute": types.StringType,
+ "value": types.StringType,
+ "name": types.StringType,
+ },
+ },
+ },
+ },
+ map[string]attr.Value{
+ "expression": attr.Value(types.StringValue("")),
+ "variables": attr.Value(types.ListNull(types.ObjectType{
+ AttrTypes: map[string]attr.Type{
+ "attribute": types.StringType,
+ "value": types.StringType,
+ "name": types.StringType,
+ },
+ })),
+ }),
+ ),
+ },
+ "score": schema.ListAttribute{
+ ElementType: types.Int64Type,
+ Optional: true,
+ Computed: true,
+ Description: "List of scores for the suppression rule.",
+ Default: listdefault.StaticValue(types.ListValueMust(types.Int64Type, []attr.Value{})),
+ },
+ "severity": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ Description: "Severity of the suppression rule.",
+ Default: stringdefault.StaticString(""),
+ },
+ "fix_available": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ Description: "Fix available for the suppression rule.",
+ Default: stringdefault.StaticString("false"),
+ },
+ "vulnerabilities": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ Description: "Vulnerabilities for the suppression rule.",
+ Default: stringdefault.StaticString(""),
+ },
+ "expiry": schema.Int64Attribute{
+ Optional: true,
+ Computed: true,
+ Description: "Expiry in days of the suppression rule.",
+ Default: int64default.StaticInt64(0),
+ },
+ "comment": schema.StringAttribute{
+ Required: true,
+ Description: "Comment for the suppression rule.",
+ },
+ "created": schema.StringAttribute{
+ Computed: true,
+ Description: "Creation date of the suppression rule.",
+ },
+ "author": schema.StringAttribute{
+ Computed: true,
+ Description: "Author of the suppression rule.",
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "status": schema.BoolAttribute{
+ Optional: true,
+ Computed: true,
+ Description: "Activation status of the suppression rule.",
+ Default: booldefault.StaticBool(true),
+ },
+ },
+ Description: "Manages a suppression rule.",
+ }
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *suppressionRuleResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
+ // Retrieve values from plan
+ var plan suppressionRuleResourceModel
+ diags := req.Plan.Get(ctx, &plan)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Generate API request body from plan
+ suppressionRule := aquasec.SuppressionRule{
+ Name: plan.Name.ValueString(),
+ Score: []int{},
+ Severity: plan.Severity.ValueString(),
+ FixAvailable: plan.FixAvailable.ValueString(),
+ Vulnerabilities: plan.Vulnerabilities.ValueString(),
+ Expiry: int(plan.Expiry.ValueInt64()),
+ Comment: plan.Comment.ValueString(),
+ Created: plan.Created.ValueString(),
+ Author: plan.Author.ValueString(),
+ Status: plan.Status.ValueBool(),
+ }
+ for _, applicationScope := range plan.ApplicationScopes {
+ suppressionRule.ApplicationScopes = append(suppressionRule.ApplicationScopes, applicationScope.ValueString())
+ }
+ if plan.Scope != nil {
+ suppressionRule.Scope = &aquasec.Scope{
+ Expression: plan.Scope.Expression.ValueString(),
+ }
+ for _, variable := range plan.Scope.Variables {
+ suppressionRule.Scope.Variables = append(suppressionRule.Scope.Variables, aquasec.Variable{
+ Attribute: variable.Attribute.ValueString(),
+ Value: variable.Value.ValueString(),
+ Name: variable.Name.ValueString(),
+ })
+ }
+ }
+ for _, score := range plan.Score {
+ suppressionRule.Score = append(suppressionRule.Score, int(score.ValueInt64()))
+ }
+
+ // Create new suppression rule
+ suppressionRuleID, err := r.client.CreateSuppressionRule(suppressionRule)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error creating suppression rule",
+ "Could not create suppression rule, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ // Update suppression rule status if it is false (default is true)
+ if !plan.Status.ValueBool() {
+ err = r.client.DisableSuppressionRule(strconv.Itoa(suppressionRuleID))
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error updating suppression rule status",
+ "Could not update suppression rule status, unexpected error: "+err.Error(),
+ )
+ return
+ }
+ }
+
+ fetchedSuppressionRule, err := r.client.GetSuppressionRule(strconv.Itoa(suppressionRuleID))
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error fetching suppression rule",
+ "Could not fetch suppression rule, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ // Map response body to schema and populate Computed attribute values
+ plan.ID = types.StringValue(strconv.Itoa(fetchedSuppressionRule.ID))
+ plan.ApplicationScopes = []types.String{}
+ plan.Scope = &suppressionRuleScopeModel{
+ Expression: types.StringValue(fetchedSuppressionRule.Scope.Expression),
+ Variables: nil,
+ }
+ plan.Score = []types.Int64{}
+ plan.Severity = types.StringValue(fetchedSuppressionRule.Severity)
+ plan.FixAvailable = types.StringValue(fetchedSuppressionRule.FixAvailable)
+ plan.Vulnerabilities = types.StringValue(fetchedSuppressionRule.Vulnerabilities)
+ plan.Expiry = types.Int64Value(int64(fetchedSuppressionRule.Expiry))
+ plan.Comment = types.StringValue(fetchedSuppressionRule.Comment)
+ plan.Created = types.StringValue(fetchedSuppressionRule.Created)
+ plan.Author = types.StringValue(fetchedSuppressionRule.Author)
+ plan.Status = types.BoolValue(fetchedSuppressionRule.Status)
+
+ for _, applicationScope := range fetchedSuppressionRule.ApplicationScopes {
+ plan.ApplicationScopes = append(plan.ApplicationScopes, types.StringValue(applicationScope))
+ }
+
+ for _, variable := range fetchedSuppressionRule.Scope.Variables {
+ variableModel := suppressionRuleVariableModel{
+ Attribute: types.StringValue(variable.Attribute),
+ Value: types.StringValue(variable.Value),
+ }
+ if variable.Name != "" {
+ variableModel.Name = types.StringValue(variable.Name)
+ }
+ plan.Scope.Variables = append(plan.Scope.Variables, variableModel)
+ }
+
+ for _, score := range fetchedSuppressionRule.Score {
+ plan.Score = append(plan.Score, types.Int64Value(int64(score)))
+ }
+
+ // Set state to fully populated data
+ diags = resp.State.Set(ctx, plan)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *suppressionRuleResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
+ // Get current state
+ var state suppressionRuleResourceModel
+ diags := req.State.Get(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Get refreshed suppression rule value from AquaSec
+ suppressionRule, err := r.client.GetSuppressionRule(state.ID.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Reading AquaSec Suppression Rule",
+ "Could not read AquaSec suppression rule ID "+state.ID.ValueString()+": "+err.Error(),
+ )
+ return
+ }
+
+ // Overwrite suppression rule with refreshed state
+ state.ID = types.StringValue(strconv.Itoa(suppressionRule.ID))
+ state.Name = types.StringValue(suppressionRule.Name)
+ state.ApplicationScopes = []types.String{}
+ state.Scope = &suppressionRuleScopeModel{
+ Expression: types.StringValue(suppressionRule.Scope.Expression),
+ Variables: nil,
+ }
+ state.Score = []types.Int64{}
+ state.Severity = types.StringValue(suppressionRule.Severity)
+ state.FixAvailable = types.StringValue(suppressionRule.FixAvailable)
+ state.Vulnerabilities = types.StringValue(suppressionRule.Vulnerabilities)
+ state.Expiry = types.Int64Value(int64(suppressionRule.Expiry))
+ state.Comment = types.StringValue(suppressionRule.Comment)
+ state.Created = types.StringValue(suppressionRule.Created)
+ state.Author = types.StringValue(suppressionRule.Author)
+ state.Status = types.BoolValue(suppressionRule.Status)
+
+ for _, applicationScope := range suppressionRule.ApplicationScopes {
+ state.ApplicationScopes = append(state.ApplicationScopes, types.StringValue(applicationScope))
+ }
+
+ for _, variable := range suppressionRule.Scope.Variables {
+ variableModel := suppressionRuleVariableModel{
+ Attribute: types.StringValue(variable.Attribute),
+ Value: types.StringValue(variable.Value),
+ }
+ if variable.Name != "" {
+ variableModel.Name = types.StringValue(variable.Name)
+ }
+ state.Scope.Variables = append(state.Scope.Variables, variableModel)
+ }
+
+ for _, score := range suppressionRule.Score {
+ state.Score = append(state.Score, types.Int64Value(int64(score)))
+ }
+
+ // Set refreshed state
+ diags = resp.State.Set(ctx, state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+// Update updates the resource and sets the updated Terraform state on success.
+func (r *suppressionRuleResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
+ // Retrieve values from plan
+ var plan, prior suppressionRuleResourceModel
+ diags := req.Plan.Get(ctx, &plan)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+ diags = req.State.Get(ctx, &prior)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Generate API request body from plan
+ id, err := strconv.Atoi(plan.ID.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Converting AquaSec Suppression Rule",
+ "Could not convert suppression rule id, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ suppressionRule := aquasec.SuppressionRule{
+ ID: id,
+ Name: plan.Name.ValueString(),
+ Score: []int{},
+ Severity: plan.Severity.ValueString(),
+ FixAvailable: plan.FixAvailable.ValueString(),
+ Vulnerabilities: plan.Vulnerabilities.ValueString(),
+ Expiry: int(plan.Expiry.ValueInt64()),
+ Comment: plan.Comment.ValueString(),
+ Created: plan.Created.ValueString(),
+ Author: plan.Author.ValueString(),
+ Status: plan.Status.ValueBool(),
+ }
+ for _, applicationScope := range plan.ApplicationScopes {
+ suppressionRule.ApplicationScopes = append(suppressionRule.ApplicationScopes, applicationScope.ValueString())
+ }
+ if plan.Scope != nil {
+ suppressionRule.Scope = &aquasec.Scope{
+ Expression: plan.Scope.Expression.ValueString(),
+ }
+ for _, variable := range plan.Scope.Variables {
+ suppressionRule.Scope.Variables = append(suppressionRule.Scope.Variables, aquasec.Variable{
+ Attribute: variable.Attribute.ValueString(),
+ Value: variable.Value.ValueString(),
+ Name: variable.Name.ValueString(),
+ })
+ }
+ }
+ for _, score := range plan.Score {
+ suppressionRule.Score = append(suppressionRule.Score, int(score.ValueInt64()))
+ }
+
+ // Update existing suppression rule
+ err = r.client.UpdateSuppressionRule(suppressionRule)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error updating suppression rule",
+ "Could not update suppression rule, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ // Update suppression rule status if it has changed
+ if plan.Status.ValueBool() != prior.Status.ValueBool() {
+ if plan.Status.ValueBool() {
+ err = r.client.ActivateSuppressionRule(plan.ID.ValueString())
+ } else {
+ err = r.client.DisableSuppressionRule(plan.ID.ValueString())
+ }
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error updating suppression rule status",
+ "Could not update suppression rule status, unexpected error: "+err.Error(),
+ )
+ return
+ }
+ }
+
+ // Fetch updated items from GetSuppressionRule as UpdateSuppressionRule does not return updated data
+ fetchedSuppressionRule, err := r.client.GetSuppressionRule(plan.ID.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error fetching suppression rule",
+ "Could not fetch suppression rule, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ // Update resource state with updated suppression rule
+ plan.ID = types.StringValue(strconv.Itoa(fetchedSuppressionRule.ID))
+ plan.ApplicationScopes = []types.String{}
+ plan.Scope = &suppressionRuleScopeModel{
+ Expression: types.StringValue(fetchedSuppressionRule.Scope.Expression),
+ Variables: nil,
+ }
+ plan.Score = []types.Int64{}
+ plan.Severity = types.StringValue(fetchedSuppressionRule.Severity)
+ plan.FixAvailable = types.StringValue(fetchedSuppressionRule.FixAvailable)
+ plan.Vulnerabilities = types.StringValue(fetchedSuppressionRule.Vulnerabilities)
+ plan.Expiry = types.Int64Value(int64(fetchedSuppressionRule.Expiry))
+ plan.Comment = types.StringValue(fetchedSuppressionRule.Comment)
+ plan.Created = types.StringValue(fetchedSuppressionRule.Created)
+ plan.Author = types.StringValue(fetchedSuppressionRule.Author)
+ plan.Status = types.BoolValue(fetchedSuppressionRule.Status)
+
+ for _, applicationScope := range fetchedSuppressionRule.ApplicationScopes {
+ plan.ApplicationScopes = append(plan.ApplicationScopes, types.StringValue(applicationScope))
+ }
+
+ for _, variable := range fetchedSuppressionRule.Scope.Variables {
+ variableModel := suppressionRuleVariableModel{
+ Attribute: types.StringValue(variable.Attribute),
+ Value: types.StringValue(variable.Value),
+ }
+ if variable.Name != "" {
+ variableModel.Name = types.StringValue(variable.Name)
+ }
+ plan.Scope.Variables = append(plan.Scope.Variables, variableModel)
+ }
+
+ for _, score := range fetchedSuppressionRule.Score {
+ plan.Score = append(plan.Score, types.Int64Value(int64(score)))
+ }
+
+ diags = resp.State.Set(ctx, plan)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+// Delete deletes the resource and removes the Terraform state on success.
+func (r *suppressionRuleResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+ // Retrieve values from state
+ var state suppressionRuleResourceModel
+ diags := req.State.Get(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Delete existing suppression rule
+ err := r.client.DeleteSuppressionRule(state.ID.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error deleting suppression rule",
+ "Could not delete suppression rule, unexpected error: "+err.Error(),
+ )
+ return
+ }
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *suppressionRuleResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
+ // Add a nil check when handling ProviderData because Terraform
+ // sets that data after it calls the ConfigureProvider RPC.
+ if req.ProviderData == nil {
+ return
+ }
+
+ client, ok := req.ProviderData.(*aquasec.Client)
+
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Data Source Configure Type",
+ fmt.Sprintf("Expected *aquasec.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+
+ return
+ }
+
+ r.client = client
+}
+
+func (r *suppressionRuleResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
+ // Retrieve import ID andn save to id attribute
+ resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
+}
diff --git a/internal/provider/suppression_rule_resource_test.go b/internal/provider/suppression_rule_resource_test.go
new file mode 100644
index 00000000..babd8cee
--- /dev/null
+++ b/internal/provider/suppression_rule_resource_test.go
@@ -0,0 +1,121 @@
+package provider
+
+import (
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "testing"
+)
+
+func TestAccSuppressionRuleResource(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ // Create and Read testing
+ {
+ Config: providerConfig + `
+resource "aquasec_suppression_rule" "test" {
+ name = "test-suppression-rule"
+ application_scopes = ["Global"]
+ scope = {
+ expression = "v1"
+ variables = [
+ {
+ attribute = "aqua.registry"
+ value = "\"Docker Hub\""
+ }
+ ]
+ }
+ score = [1]
+ severity = "test-severity"
+ fix_available = "false"
+ vulnerabilities = "test-vulnerabilities"
+ expiry = 1
+ comment = "test-comment"
+ status = true
+}
+`,
+ Check: resource.ComposeAggregateTestCheckFunc(
+ // Verity suppression rule is created
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "name", "test-suppression-rule"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "application_scopes.#", "1"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "application_scopes.0", "Global"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "scope.expression", "v1"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "scope.variables.#", "1"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "scope.variables.0.attribute", "aqua.registry"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "scope.variables.0.value", "\"Docker Hub\""),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "score.#", "1"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "score.0", "1"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "severity", "test-severity"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "fix_available", "false"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "vulnerabilities", "test-vulnerabilities"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "expiry", "1"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "comment", "test-comment"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "status", "true"),
+ // Verify dynamic values have any value set in the state
+ resource.TestCheckResourceAttrSet("aquasec_suppression_rule.test", "id"),
+ resource.TestCheckResourceAttrSet("aquasec_suppression_rule.test", "created"),
+ resource.TestCheckResourceAttrSet("aquasec_suppression_rule.test", "author"),
+ ),
+ },
+ // ImportState testing
+ {
+ ResourceName: "aquasec_suppression_rule.test",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // Update and Read testing
+ {
+ Config: providerConfig + `
+resource "aquasec_suppression_rule" "test" {
+ name = "test-suppression-rule-updated"
+ application_scopes = ["Global"]
+ scope = {
+ expression = "v1 && v2"
+ variables = [
+ {
+ attribute = "image.repo"
+ value = "vpu/*"
+ },
+ {
+ attribute = "image.name"
+ value = "vpu/*-direkt*"
+ }
+ ]
+ }
+ score = [2]
+ severity = "test-severity-updated"
+ fix_available = "true"
+ vulnerabilities = "test-vulnerabilities-updated"
+ expiry = 2
+ comment = "test-comment-updated"
+ status = false
+}
+`,
+ Check: resource.ComposeAggregateTestCheckFunc(
+ // Verify suppression rule is updated
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "name", "test-suppression-rule-updated"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "application_scopes.#", "1"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "application_scopes.0", "Global"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "scope.expression", "v1 && v2"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "scope.variables.#", "2"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "scope.variables.0.attribute", "image.repo"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "scope.variables.0.value", "vpu/*"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "scope.variables.1.attribute", "image.name"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "scope.variables.1.value", "vpu/*-direkt*"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "score.#", "1"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "score.0", "2"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "severity", "test-severity-updated"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "fix_available", "true"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "vulnerabilities", "test-vulnerabilities-updated"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "expiry", "2"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "comment", "test-comment-updated"),
+ resource.TestCheckResourceAttr("aquasec_suppression_rule.test", "status", "false"),
+ // Verify dynamic values have any value set in the state
+ resource.TestCheckResourceAttrSet("aquasec_suppression_rule.test", "id"),
+ resource.TestCheckResourceAttrSet("aquasec_suppression_rule.test", "created"),
+ resource.TestCheckResourceAttrSet("aquasec_suppression_rule.test", "author"),
+ ),
+ },
+ // Delete testing automatically occurs in TestCase
+ },
+ })
+}
diff --git a/internal/provider/suppression_rules_data_source.go b/internal/provider/suppression_rules_data_source.go
new file mode 100644
index 00000000..d241fab7
--- /dev/null
+++ b/internal/provider/suppression_rules_data_source.go
@@ -0,0 +1,243 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+
+ aquasec "github.com/aquasecurity/terraform-provider-aquasec/client"
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+ _ datasource.DataSource = &suppressionRulesDataSource{}
+ _ datasource.DataSourceWithConfigure = &suppressionRulesDataSource{}
+)
+
+// NewSuppressionRulesDataSource is a helper function to simplify the provider implementation.
+func NewSuppressionRulesDataSource() datasource.DataSource {
+ return &suppressionRulesDataSource{}
+}
+
+// suppressionRulesDataSource is the data source implementation.
+type suppressionRulesDataSource struct {
+ client *aquasec.Client
+}
+
+// suppressionRulesDataSourceModel maps the data source schema data.
+type suppressionRulesDataSourceModel struct {
+ SuppressionRules []suppressionRulesModel `tfsdk:"suppression_rules"`
+}
+
+// suppressionRulesModel maps suppression rules schema data.
+type suppressionRulesModel struct {
+ ID types.Int64 `tfsdk:"id"`
+ Name types.String `tfsdk:"name"`
+ ApplicationScopes []types.String `tfsdk:"application_scopes"`
+ Scope suppressionRulesScopesModel `tfsdk:"scope"`
+ Score []types.Int64 `tfsdk:"score"`
+ Severity types.String `tfsdk:"severity"`
+ FixAvailable types.String `tfsdk:"fix_available"`
+ Vulnerabilities types.String `tfsdk:"vulnerabilities"`
+ Expiry types.Int64 `tfsdk:"expiry"`
+ Comment types.String `tfsdk:"comment"`
+ Created types.String `tfsdk:"created"`
+ Author types.String `tfsdk:"author"`
+ Status types.Bool `tfsdk:"status"`
+}
+
+// suppressionRulesScopesModel maps suppression rule scopes data.
+type suppressionRulesScopesModel struct {
+ Expression types.String `tfsdk:"expression"`
+ Variables []suppressionRulesVariablesModel `tfsdk:"variables"`
+}
+
+// suppressionRulesVariablesModel maps suppression rule variables data.
+type suppressionRulesVariablesModel struct {
+ Attribute types.String `tfsdk:"attribute"`
+ Value types.String `tfsdk:"value"`
+ Name types.String `tfsdk:"name"`
+}
+
+// Metadata returns the data source type name.
+func (d *suppressionRulesDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_suppression_rules"
+}
+
+// Schema defines the schema for the data source.
+func (d *suppressionRulesDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ Attributes: map[string]schema.Attribute{
+ "suppression_rules": schema.ListNestedAttribute{
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "id": schema.Int64Attribute{
+ Computed: true,
+ Description: "Identifier used by AquaSec to identify the suppression rule.",
+ },
+ "name": schema.StringAttribute{
+ Computed: true,
+ Description: "Name of the suppression rule.",
+ },
+ "application_scopes": schema.ListAttribute{
+ Computed: true,
+ ElementType: types.StringType,
+ Description: "List of application scopes for the suppression rule.",
+ },
+ "scope": schema.SingleNestedAttribute{
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "expression": schema.StringAttribute{
+ Computed: true,
+ Description: "Expression of the suppression rule.",
+ },
+ "variables": schema.ListNestedAttribute{
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "attribute": schema.StringAttribute{
+ Computed: true,
+ Description: "Attribute of the variable.",
+ },
+ "value": schema.StringAttribute{
+ Computed: true,
+ Description: "Value of the variable.",
+ },
+ "name": schema.StringAttribute{
+ Computed: true,
+ Description: "Name of the variable.",
+ },
+ },
+ },
+ Description: "Variables of the suppression rule.",
+ },
+ },
+ Description: "Scope of the suppression rule.",
+ },
+ "score": schema.ListAttribute{
+ Computed: true,
+ ElementType: types.Int64Type,
+ Description: "List of scores for the suppression rule.",
+ },
+ "severity": schema.StringAttribute{
+ Computed: true,
+ Description: "Severity of the suppression rule.",
+ },
+ "fix_available": schema.StringAttribute{
+ Computed: true,
+ Description: "Fix available for the suppression rule.",
+ },
+ "vulnerabilities": schema.StringAttribute{
+ Computed: true,
+ Description: "Vulnerabilities as comma separated list for the suppression rule.",
+ },
+ "expiry": schema.Int64Attribute{
+ Computed: true,
+ Description: "Expiry in days of the suppression rule.",
+ },
+ "comment": schema.StringAttribute{
+ Computed: true,
+ Description: "Comment for the suppression rule.",
+ },
+ "created": schema.StringAttribute{
+ Computed: true,
+ Description: "Creation date of the suppression rule.",
+ },
+ "author": schema.StringAttribute{
+ Computed: true,
+ Description: "Author of the suppression rule.",
+ },
+ "status": schema.BoolAttribute{
+ Computed: true,
+ Description: "Status of the suppression rule.",
+ },
+ },
+ },
+ },
+ },
+ Description: "Fetches the list of suppression rules.",
+ }
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (d *suppressionRulesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ var state suppressionRulesDataSourceModel
+
+ suppressionRules, err := d.client.GetSuppressionRules()
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to Read AquaSec Suppression Rules",
+ err.Error(),
+ )
+ return
+ }
+
+ // Map response body to model
+ state.SuppressionRules = []suppressionRulesModel{}
+ for _, suppressionRule := range suppressionRules {
+ suppressionRuleState := suppressionRulesModel{
+ ID: types.Int64Value(int64(suppressionRule.ID)),
+ Name: types.StringValue(suppressionRule.Name),
+ Scope: suppressionRulesScopesModel{
+ Expression: types.StringValue(suppressionRule.Scope.Expression),
+ },
+ Severity: types.StringValue(suppressionRule.Severity),
+ FixAvailable: types.StringValue(suppressionRule.FixAvailable),
+ Vulnerabilities: types.StringValue(suppressionRule.Vulnerabilities),
+ Expiry: types.Int64Value(int64(suppressionRule.Expiry)),
+ Comment: types.StringValue(suppressionRule.Comment),
+ //Created: types.StringValue(suppressionRule.Created),
+ Author: types.StringValue(suppressionRule.Author),
+ Status: types.BoolValue(suppressionRule.Status),
+ }
+
+ for _, applicationScope := range suppressionRule.ApplicationScopes {
+ suppressionRuleState.ApplicationScopes = append(suppressionRuleState.ApplicationScopes, types.StringValue(applicationScope))
+ }
+
+ for _, variable := range suppressionRule.Scope.Variables {
+ suppressionRuleState.Scope.Variables = append(suppressionRuleState.Scope.Variables, suppressionRulesVariablesModel{
+ Attribute: types.StringValue(variable.Attribute),
+ Value: types.StringValue(variable.Value),
+ Name: types.StringValue(variable.Name),
+ })
+ }
+
+ for _, score := range suppressionRule.Score {
+ suppressionRuleState.Score = append(suppressionRuleState.Score, types.Int64Value(int64(score)))
+ }
+
+ state.SuppressionRules = append(state.SuppressionRules, suppressionRuleState)
+ }
+
+ // Set state
+ diags := resp.State.Set(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+// Configure adds the provider configured client to the data source.
+func (d *suppressionRulesDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+ // Add a nil check when handling ProviderData because Terraform
+ // sets that data after it calls the ConfigureProvider RPC.
+ if req.ProviderData == nil {
+ return
+ }
+
+ client, ok := req.ProviderData.(*aquasec.Client)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Data Source Configure Type",
+ fmt.Sprintf("Expected *aquasec.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+
+ return
+ }
+
+ d.client = client
+}
diff --git a/internal/provider/suppression_rules_data_source_test.go b/internal/provider/suppression_rules_data_source_test.go
new file mode 100644
index 00000000..42949d06
--- /dev/null
+++ b/internal/provider/suppression_rules_data_source_test.go
@@ -0,0 +1,23 @@
+package provider
+
+import (
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccSuppressionRulesDataSource(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ // Read testing
+ {
+ Config: providerConfig + `data "aquasec_suppression_rules" "test" {}`,
+ Check: resource.ComposeAggregateTestCheckFunc(
+ // Verify number of suppression rules returned
+ resource.TestCheckResourceAttr("data.aquasec_suppression_rules.test", "suppression_rules.#", "0"),
+ ),
+ },
+ },
+ })
+}
diff --git a/main.go b/main.go
index e406aa2c..96764702 100644
--- a/main.go
+++ b/main.go
@@ -1,17 +1,64 @@
package main
import (
+ "context"
+ "flag"
+ "log"
+
"github.com/aquasecurity/terraform-provider-aquasec/aquasec"
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
- "github.com/hashicorp/terraform-plugin-sdk/v2/plugin"
+ "github.com/aquasecurity/terraform-provider-aquasec/internal/provider"
+ "github.com/hashicorp/terraform-plugin-framework/providerserver"
+ "github.com/hashicorp/terraform-plugin-go/tfprotov6"
+ "github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server"
+ "github.com/hashicorp/terraform-plugin-mux/tf5to6server"
+ "github.com/hashicorp/terraform-plugin-mux/tf6muxserver"
)
var version string
func main() {
- plugin.Serve(&plugin.ServeOpts{
- ProviderFunc: func() *schema.Provider {
- return aquasec.Provider(version)
+ ctx := context.Background()
+
+ var debug bool
+
+ flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve")
+ flag.Parse()
+
+ upgradedSdkServer, err := tf5to6server.UpgradeServer(
+ ctx,
+ aquasec.Provider(version).GRPCProvider,
+ )
+
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ providers := []func() tfprotov6.ProviderServer{
+ providerserver.NewProtocol6(provider.New(version)()),
+ func() tfprotov6.ProviderServer {
+ return upgradedSdkServer
},
- })
+ }
+
+ muxServer, err := tf6muxserver.NewMuxServer(ctx, providers...)
+
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ var serveOpts []tf6server.ServeOpt
+
+ if debug {
+ serveOpts = append(serveOpts, tf6server.WithManagedDebug())
+ }
+
+ err = tf6server.Serve(
+ "registry.terraform.io/aquasecurity/aquasec",
+ muxServer.ProviderServer,
+ serveOpts...,
+ )
+
+ if err != nil {
+ log.Fatal(err)
+ }
}