diff --git a/README.md b/README.md index 2b1b6010..cab34c70 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Full and comprehensive documentation for this provider with detailed description Please refer to our [Change Log](CHANGELOG.md) to learn about our version history, its features, bug fixes and Control Plane compatibility. -## Building, Documenting and Testing this Project +## Building, Maintaining, Documenting and Testing this Project ### Build Instructions @@ -20,7 +20,7 @@ In order to build this repository, follow the steps below: 1. Clone [terraform-provider-cyral](https://github.com/cyralinc/terraform-provider-cyral) repo from GitHub; -2. Go to the root directory of the cloned repo using Linux shell and execute `make`. The build process will create binaries in directory `out` for both `darwin` and `linux` 64 bits. These binaries will be copied automatically to the local Terraform registry to be used by Terraform 13 and later. +2. Go to the root directory of the cloned repo using Linux shell and execute `make`. The build process will create binaries in directory `out` for both `darwin` and `linux` 64 bits. These binaries will be copied automatically to the local Terraform registry to be used by Terraform 0.13 and later. Alternatively, you can use the dockerfile to build the image using `make docker-compose/build` @@ -36,6 +36,11 @@ terraform { } ``` +### Adding new resources or data sources + +Use the abstractions provided in the package `core` to add new resources or data sources. +Read the documentation in [`cyral.core`](./cyral/core/README.md) for more information. + ### Updating the Documentation This project uses [`terraform-plugin-docs`](https://github.com/hashicorp/terraform-plugin-docs). @@ -104,12 +109,4 @@ pre-commit install ### Running Project Built Locally -#### Terraform 0.12 - -Copy the desired binary file created in directory `out` (see [Build Instructions](#build-instructions)) to the root folder containing those `.tf` files that will be used to handle Cyral Terraform provider resources. - -Run `terraform init` and proceed with `terraform apply` normally to execute your Terraform scripts. - -#### Terraform 0.13+ - Build the project using steps in [Build Instructions](#build-instructions), then proceed normally with `terraform init` and `terraform apply` commands. diff --git a/client/client.go b/cyral/client/client.go similarity index 75% rename from client/client.go rename to cyral/client/client.go index 893740db..b84c19eb 100644 --- a/client/client.go +++ b/cyral/client/client.go @@ -8,6 +8,8 @@ import ( "io/ioutil" "log" "net/http" + "os" + "strconv" "strings" "golang.org/x/oauth2" @@ -16,6 +18,13 @@ import ( const redactedString = "**********" +const ( + EnvVarClientID = "CYRAL_TF_CLIENT_ID" + EnvVarClientSecret = "CYRAL_TF_CLIENT_SECRET" + EnvVarCPURL = "CYRAL_TF_CONTROL_PLANE" + EnvVarTLSSkipVerify = "CYRAL_TF_TLS_SKIP_VERIFY" +) + // Client stores data for all existing resources. Also, this is // the struct that is passed along resources CRUD operations. type Client struct { @@ -24,9 +33,9 @@ type Client struct { client *http.Client } -// NewClient configures and returns a fully initialized Client. -func NewClient(clientID, clientSecret, controlPlane string, tlsSkipVerify bool) (*Client, error) { - log.Printf("[DEBUG] Init NewClient") +// New configures and returns a fully initialized Client. +func New(clientID, clientSecret, controlPlane string, tlsSkipVerify bool) (*Client, error) { + log.Printf("[DEBUG] Init client.New") if clientID == "" || clientSecret == "" || controlPlane == "" { return nil, fmt.Errorf("clientID, clientSecret and controlPlane must have non-empty values") @@ -49,7 +58,7 @@ func NewClient(clientID, clientSecret, controlPlane string, tlsSkipVerify bool) tokenSource := tokenConfig.TokenSource(context.Background()) log.Printf("[DEBUG] TokenSource: %v", tokenSource) - log.Printf("[DEBUG] End NewClient") + log.Printf("[DEBUG] End client.New") return &Client{ ControlPlane: controlPlane, @@ -141,3 +150,38 @@ func redactContent(content string) string { } return redactedString } + +func FromEnv() (*Client, error) { + clientID, clientSecret, controlPlane, tlsSkipVerify, err := + getProviderConfigFromEnv() + if err != nil { + return nil, fmt.Errorf("unable to create Cyral client: %w", err) + } + c, err := New(clientID, clientSecret, controlPlane, + tlsSkipVerify) + if err != nil { + return nil, fmt.Errorf("unable to create Cyral client: %w", err) + } + return c, nil +} + +func getProviderConfigFromEnv() ( + clientID string, + clientSecret string, + controlPlane string, + tlsSkipVerify bool, + err error, +) { + clientID = os.Getenv(EnvVarClientID) + clientSecret = os.Getenv(EnvVarClientSecret) + controlPlane = os.Getenv(EnvVarCPURL) + tlsSkipVerifyStr := os.Getenv(EnvVarTLSSkipVerify) + if tlsSkipVerifyStr != "" { + tlsSkipVerify, err = strconv.ParseBool(tlsSkipVerifyStr) + if err != nil { + return "", "", "", false, fmt.Errorf("invalid value for "+ + "env var %q: %w", EnvVarTLSSkipVerify, err) + } + } + return +} diff --git a/client/client_test.go b/cyral/client/client_test.go similarity index 84% rename from client/client_test.go rename to cyral/client/client_test.go index abf8c4e1..0e07d7e1 100644 --- a/client/client_test.go +++ b/cyral/client/client_test.go @@ -15,7 +15,7 @@ func TestNewClient_WhenTLSSkipVerifyIsEnabled_ThenInsecureSkipVerifyIsTrue(t *te controlPlane := "someControlPlane" tlsSkipVerify := true - client, err := NewClient(clientID, clientSecret, controlPlane, tlsSkipVerify) + client, err := New(clientID, clientSecret, controlPlane, tlsSkipVerify) require.NoError(t, err) @@ -37,7 +37,7 @@ func TestNewClient_WhenTLSSkipVerifyIsDisabled_ThenInsecureSkipVerifyIsFalse(t * controlPlane := "someControlPlane" tlsSkipVerify := false - client, err := NewClient(clientID, clientSecret, controlPlane, tlsSkipVerify) + client, err := New(clientID, clientSecret, controlPlane, tlsSkipVerify) require.NoError(t, err) @@ -59,7 +59,7 @@ func TestNewClient_WhenClientIDIsEmpty_ThenThrowError(t *testing.T) { controlPlane := "someControlPlane" tlsSkipVerify := false - client, err := NewClient(clientID, clientSecret, controlPlane, tlsSkipVerify) + client, err := New(clientID, clientSecret, controlPlane, tlsSkipVerify) expectedErrorMessage := "clientID, clientSecret and controlPlane must have non-empty values" @@ -73,7 +73,7 @@ func TestNewClient_WhenClientSecretIsEmpty_ThenThrowError(t *testing.T) { controlPlane := "someControlPlane" tlsSkipVerify := false - client, err := NewClient(clientID, clientSecret, controlPlane, tlsSkipVerify) + client, err := New(clientID, clientSecret, controlPlane, tlsSkipVerify) expectedErrorMessage := "clientID, clientSecret and controlPlane must have non-empty values" @@ -87,7 +87,7 @@ func TestNewClient_WhenControlPlaneIsEmpty_ThenThrowError(t *testing.T) { controlPlane := "" tlsSkipVerify := false - client, err := NewClient(clientID, clientSecret, controlPlane, tlsSkipVerify) + client, err := New(clientID, clientSecret, controlPlane, tlsSkipVerify) expectedErrorMessage := "clientID, clientSecret and controlPlane must have non-empty values" diff --git a/client/error.go b/cyral/client/error.go similarity index 100% rename from client/error.go rename to cyral/client/error.go diff --git a/client/validation.go b/cyral/client/validation.go similarity index 100% rename from client/validation.go rename to cyral/client/validation.go diff --git a/cyral/core/README.md b/cyral/core/README.md new file mode 100644 index 00000000..aea7e0bc --- /dev/null +++ b/cyral/core/README.md @@ -0,0 +1,201 @@ +# Cyral Provider Core + +The `core` package was created in order to put together all the code that is responsible +for managing the Provider itself and to provide reusable functions and abstractions +for resources and data sources. + +## How to use to create new resources and data sources + +There are some main types that must be used to create a new resources and data sources: +`SchemaDescriptor`, `PackageSchema`, `ResourceData`, `ResponseData` and +`ResourceOperationConfig`. In a nutshell, these abstractions provide the means to +teach the provider how to interact with the API, how to describe the feature as a +Terraform resource/data source and finally teach the provider how to perform the +translation from API to Terraform schema and vice-versa. + +Use the files below as examples to create your own implementation. It is advised that +you follow the same naming convention for all the files to simplify future code changes. + +### model.go + +```go +// model.go +package newfeature + +type NewFeature struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` +} + +func (r *NewFeature) WriteToSchema(d *schema.ResourceData) error { + if err := d.Set("description", r.Description); err != nil { + return fmt.Errorf("error setting 'description' field: %w", err) + } + d.SetId(r.Name) + return nil +} + +func (r *NewFeature) ReadFromSchema(d *schema.ResourceData) error { + r.Name = d.Get("name").(string) + r.Description = d.Get("description").(string) + return nil +} +``` + +### datasource.go + +```go +// datasource.go +package newfeature + +func dataSourceSchema() *schema.Resource { + return &schema.Resource{ + Description: "Some description.", + ReadContext: core.ReadResource(core.ResourceOperationConfig{ + Name: "NewFeatureRead", + HttpMethod: http.MethodGet, + CreateURL: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/NewFeature/%s", c.ControlPlane, d.Get("name").(string)) + }, + NewResponseData: func(d *schema.ResourceData) core.ResponseData { + return &NewFeature{} + }, + }), + Schema: map[string]*schema.Schema{ + "name": { + Description: "Retrieve the unique label with this name, if it exists.", + Type: schema.TypeString, + Optional: true, + }, + "description": { + Description: "Description of the data source.", + Type: schema.TypeString, + Optional: true, + }, + }, + } +} +``` + +### resource.go + +```go +// resource.go +package newfeature + +func resourceSchema() *schema.Resource { + return &schema.Resource{ + Description: "Some description.", + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ + Name: "NewFeatureResourceRead", + HttpMethod: http.MethodPost, + CreateURL: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/NewFeature", c.ControlPlane) + }, + NewResponseData: func(d *schema.ResourceData) core.ResponseData { + return &NewFeature{} + }, + }, ReadNewFeatureConfig, + ), + ReadContext: core.ReadResource(ReadNewFeatureConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ + Name: "NewFeatureUpdate", + HttpMethod: http.MethodPut, + CreateURL: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/NewFeature/%s", c.ControlPlane, d.Id()) + }, + NewResourceData: func() core.ResourceData { return &NewFeature{} }, + }, ReadNewFeatureConfig, + ), + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ + Name: "NewFeatureDelete", + HttpMethod: http.MethodDelete, + CreateURL: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/NewFeature/%s", c.ControlPlane, d.Id()) + }, + }, + ), + Schema: map[string]*schema.Schema{ + "name": { + Description: "...", + Type: schema.TypeString, + Optional: true, + }, + "description": { + Description: "...", + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +var ReadNewFeatureConfig = core.ResourceOperationConfig{ + Name: "NewFeatureRead", + HttpMethod: http.MethodGet, + CreateURL: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/NewFeature/%s", c.ControlPlane, d.Id()) + }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &NewFeature{} }, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "NewFeature"}, +} +``` + +### schema_loader.go + +```go +// schema_loader.go +package newfeature + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "newfeature" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: "cyral_newfeature", + Type: core.DataSourceSchemaType, + Schema: dataSourceSchema, + }, + { + Name: "cyral_newfeature", + Type: core.ResourceSchemaType, + Schema: resourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} +``` + +### provider/schema_loader.go + +Edit the existing `cyral/provider/schema_loader.go` file and add your new package schema +to function `packagesSchemas` as follows: + +```go +package provider + +import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + ... + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/newfeature" +) + +func packagesSchemas() []core.PackageSchema { + v := []core.PackageSchema{ + ..., + newfeature.PackageSchema(), + } + return v +} +``` diff --git a/cyral/core/constants.go b/cyral/core/constants.go new file mode 100644 index 00000000..5ae75d4a --- /dev/null +++ b/cyral/core/constants.go @@ -0,0 +1,10 @@ +package core + +type OperationType string + +const ( + OperationTypeCreate = OperationType("create") + OperationTypeRead = OperationType("read") + OperationTypeUpdate = OperationType("update") + OperationTypeDelete = OperationType("delete") +) diff --git a/cyral/error_handlers.go b/cyral/core/error_handlers.go similarity index 79% rename from cyral/error_handlers.go rename to cyral/core/error_handlers.go index d28c1f29..3491b350 100644 --- a/cyral/error_handlers.go +++ b/cyral/core/error_handlers.go @@ -1,4 +1,4 @@ -package cyral +package core import ( "log" @@ -6,11 +6,11 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" ) type DeleteIgnoreHttpNotFound struct { - resName string + ResName string } func (h *DeleteIgnoreHttpNotFound) HandleError( @@ -22,12 +22,12 @@ func (h *DeleteIgnoreHttpNotFound) HandleError( if !ok || httpError.StatusCode != http.StatusNotFound { return err } - log.Printf("[DEBUG] %s not found. Skipping deletion.", h.resName) + log.Printf("[DEBUG] %s not found. Skipping deletion.", h.ResName) return nil } type ReadIgnoreHttpNotFound struct { - resName string + ResName string } func (h *ReadIgnoreHttpNotFound) HandleError( @@ -40,6 +40,6 @@ func (h *ReadIgnoreHttpNotFound) HandleError( return err } r.SetId("") - log.Printf("[DEBUG] %s not found. Marking resource for recreation.", h.resName) + log.Printf("[DEBUG] %s not found. Marking resource for recreation.", h.ResName) return nil } diff --git a/cyral/resource.go b/cyral/core/resource.go similarity index 60% rename from cyral/resource.go rename to cyral/core/resource.go index 827a8176..4faecb57 100644 --- a/cyral/resource.go +++ b/cyral/core/resource.go @@ -1,4 +1,4 @@ -package cyral +package core import ( "context" @@ -6,20 +6,12 @@ import ( "fmt" "log" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -const ( - create = OperationType("create") - read = OperationType("read") - update = OperationType("update") - delete = OperationType("delete") -) - -type OperationType string - type ResourceOperation struct { Type OperationType Config ResourceOperationConfig @@ -31,20 +23,51 @@ type RequestErrorHandler interface { HandleError(d *schema.ResourceData, c *client.Client, err error) error } +// TODO Rename as `SchemaReader` and document properly. +// Teaches a resource or data source how to read from the Terraform schema and +// store in the data structure defined for it. type ResourceData interface { ReadFromSchema(d *schema.ResourceData) error } +// TODO Rename as `SchemaWriter` and document properly. +// Teaches a resource or data source how to write to the Terraform schema from +// the data stored in the data structure defined for it. type ResponseData interface { WriteToSchema(d *schema.ResourceData) error } +type SchemaType string + +const ( + DataSourceSchemaType = SchemaType("dataSource") + ResourceSchemaType = SchemaType("resource") +) + +// The `SchemaDescriptor` describes the resource for a given schema. +type SchemaDescriptor struct { + // Resource or data source name + Name string + Type SchemaType + Schema func() *schema.Resource +} + +// The `PackageSchema` is used to centralize the description of the existing +// schemas in a given package. It should be implemented in the `schema.go` +// file of a given package. +type PackageSchema interface { + Name() string + Schemas() []*SchemaDescriptor +} + type ResourceOperationConfig struct { Name string HttpMethod string CreateURL URLCreatorFunc RequestErrorHandler NewResourceData func() ResourceData + // TODO provide a default implementation returning the IDBasedResponse: + // func(_ *schema.ResourceData) core.ResponseData { return &core.IDBasedResponse{} } NewResponseData func(d *schema.ResourceData) ResponseData } @@ -56,11 +79,11 @@ func CreateResource(createConfig, readConfig ResourceOperationConfig) schema.Cre return HandleRequests( []ResourceOperation{ { - Type: create, + Type: OperationTypeCreate, Config: createConfig, }, { - Type: read, + Type: OperationTypeRead, Config: readConfig, }, }, @@ -71,7 +94,7 @@ func ReadResource(readConfig ResourceOperationConfig) schema.ReadContextFunc { return HandleRequests( []ResourceOperation{ { - Type: read, + Type: OperationTypeRead, Config: readConfig, }, }, @@ -82,11 +105,11 @@ func UpdateResource(updateConfig, readConfig ResourceOperationConfig) schema.Upd return HandleRequests( []ResourceOperation{ { - Type: update, + Type: OperationTypeUpdate, Config: updateConfig, }, { - Type: read, + Type: OperationTypeRead, Config: readConfig, }, }, @@ -97,7 +120,7 @@ func DeleteResource(deleteConfig ResourceOperationConfig) schema.DeleteContextFu return HandleRequests( []ResourceOperation{ { - Type: delete, + Type: OperationTypeDelete, Config: deleteConfig, }, }, @@ -115,12 +138,14 @@ func HandleRequests( var resourceData ResourceData if operation.Config.NewResourceData != nil { if resourceData = operation.Config.NewResourceData(); resourceData != nil { + log.Printf("[DEBUG] Calling ReadFromSchema. Schema: %#v", d) if err := resourceData.ReadFromSchema(d); err != nil { - return createError( - fmt.Sprintf("Unable to %s resource", operation.Type), + return utils.CreateError( + fmt.Sprintf("Unable to %s resource %s", operation.Type, operation.Config.Name), err.Error(), ) } + log.Printf("[DEBUG] Succesful call to ReadFromSchema. resourceData: %#v", resourceData) } } @@ -131,25 +156,27 @@ func HandleRequests( err = operation.Config.RequestErrorHandler.HandleError(d, c, err) } if err != nil { - return createError( - fmt.Sprintf("Unable to %s resource", operation.Type), + return utils.CreateError( + fmt.Sprintf("Unable to %s resource %s", operation.Type, operation.Config.Name), err.Error(), ) } if body != nil && operation.Config.NewResponseData != nil { if responseData := operation.Config.NewResponseData(d); responseData != nil { + log.Printf("[DEBUG] NewResponseData function call performed. d: %#v", d) if err := json.Unmarshal(body, responseData); err != nil { - return createError("Unable to unmarshall JSON", err.Error()) + return utils.CreateError("Unable to unmarshall JSON", err.Error()) } log.Printf("[DEBUG] Response body (unmarshalled): %#v", responseData) - + log.Printf("[DEBUG] Calling WriteToSchema: responseData: %#v", responseData) if err := responseData.WriteToSchema(d); err != nil { - return createError( - fmt.Sprintf("Unable to %s resource", operation.Type), + return utils.CreateError( + fmt.Sprintf("Unable to %s resource %s", operation.Type, operation.Config.Name), err.Error(), ) } + log.Printf("[DEBUG] Succesful call to WriteToSchema. d: %#v", d) } } diff --git a/cyral/README.md b/cyral/internal/README.md similarity index 74% rename from cyral/README.md rename to cyral/internal/README.md index d66a7e0d..6a467cd2 100644 --- a/cyral/README.md +++ b/cyral/internal/README.md @@ -1,13 +1,13 @@ ## Notes on testing -Please make sure to use the function `accTestName` for all control plane +Please make sure to use the function `utils.AccTestName` for all control plane resource names, such as repository or sidecar names. This way, we can have consistent prefixes for all resource names, which enables Terraform test sweepers, avoids name clashes, and facilitates developing and maintaining test -code. If `accTestName` is generating a name that is invalid for a particular +code. If `utils.AccTestName` is generating a name that is invalid for a particular resource type, please mention it in the code explicitly through comments, and adjust the test sweeper. We can also create other functions, such as -`accTestNameUnderscore`, in the future, if necessary. +`utils.AccTestNameUnderscore`, in the future, if necessary. If you wish to test what Terraform calls an _in-place update_, be sure that the resource is not being recreated in between the test steps. For example, if the diff --git a/cyral/internal/datalabel/classificationrule/constants.go b/cyral/internal/datalabel/classificationrule/constants.go new file mode 100644 index 00000000..97f02878 --- /dev/null +++ b/cyral/internal/datalabel/classificationrule/constants.go @@ -0,0 +1,43 @@ +package classificationrule + +import "github.com/cyralinc/terraform-provider-cyral/cyral/utils" + +type Type string + +const ( + Unknown = Type("UNKNOWN") + Rego = Type("REGO") +) + +type Status string + +const ( + Enabled = Status("ENABLED") + Disabled = Status("DISABLED") +) + +func Types() []Type { + return []Type{ + Unknown, + Rego, + } +} + +func TypesAsString() []string { + return utils.ToSliceOfString[Type](Types(), func(t Type) string { + return string(t) + }) +} + +func Statuses() []Status { + return []Status{ + Enabled, + Disabled, + } +} + +func StatusesAsString() []string { + return utils.ToSliceOfString[Status](Statuses(), func(s Status) string { + return string(s) + }) +} diff --git a/cyral/internal/datalabel/classificationrule/model.go b/cyral/internal/datalabel/classificationrule/model.go new file mode 100644 index 00000000..c85faabc --- /dev/null +++ b/cyral/internal/datalabel/classificationrule/model.go @@ -0,0 +1,18 @@ +package classificationrule + +type ClassificationRule struct { + RuleType string `json:"ruleType"` + RuleCode string `json:"ruleCode"` + RuleStatus string `json:"status"` +} + +func (dl *ClassificationRule) AsInterface() []interface{} { + if dl == nil { + return nil + } + return []interface{}{map[string]interface{}{ + "rule_type": dl.RuleType, + "rule_code": dl.RuleCode, + "rule_status": dl.RuleStatus, + }} +} diff --git a/cyral/internal/datalabel/constants.go b/cyral/internal/datalabel/constants.go new file mode 100644 index 00000000..c6eeb763 --- /dev/null +++ b/cyral/internal/datalabel/constants.go @@ -0,0 +1,26 @@ +package datalabel + +import "github.com/cyralinc/terraform-provider-cyral/cyral/utils" + +type Type string + +const ( + TypeUnknown = Type("UNKNOWN") + Predefined = Type("PREDEFINED") + Custom = Type("CUSTOM") + Default = TypeUnknown +) + +func Types() []Type { + return []Type{ + TypeUnknown, + Predefined, + Custom, + } +} + +func TypesAsString() []string { + return utils.ToSliceOfString[Type](Types(), func(t Type) string { + return string(t) + }) +} diff --git a/cyral/data_source_cyral_datalabel.go b/cyral/internal/datalabel/datasource.go similarity index 79% rename from cyral/data_source_cyral_datalabel.go rename to cyral/internal/datalabel/datasource.go index 646b2f31..45e0cd5b 100644 --- a/cyral/data_source_cyral_datalabel.go +++ b/cyral/internal/datalabel/datasource.go @@ -1,4 +1,4 @@ -package cyral +package datalabel import ( "fmt" @@ -8,81 +8,15 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" ) -type GetDataLabelResponse DataLabel - -func (resp *GetDataLabelResponse) WriteToSchema(d *schema.ResourceData) error { - if err := writeDataLabelsToDataSourceSchema([]*DataLabel{(*DataLabel)(resp)}, d); err != nil { - return err - } - d.SetId(uuid.New().String()) - return nil -} - -type GetDataLabelsResponse struct { - Labels []*DataLabel `json:"labels"` -} - -func (resp *GetDataLabelsResponse) WriteToSchema(d *schema.ResourceData) error { - if err := writeDataLabelsToDataSourceSchema(resp.Labels, d); err != nil { - return err - } - d.SetId(uuid.New().String()) - return nil -} - -func writeDataLabelsToDataSourceSchema(labels []*DataLabel, d *schema.ResourceData) error { - var labelsList []interface{} - for _, label := range labels { - labelsList = append(labelsList, map[string]interface{}{ - "name": label.Name, - "description": label.Description, - "type": label.Type, - "tags": label.TagsAsInterface(), - "classification_rule": label.ClassificationRuleAsInterface(), - "implicit": label.Implicit, - }) - } - if err := d.Set("datalabel_list", labelsList); err != nil { - return err - } - return nil -} - -func dataSourceDatalabelReadConfig() ResourceOperationConfig { - return ResourceOperationConfig{ - Name: "DatalabelDataSourceRead", - HttpMethod: http.MethodGet, - CreateURL: func(d *schema.ResourceData, c *client.Client) string { - nameFilter := d.Get("name").(string) - typeFilter := d.Get("type").(string) - var pathParams string - if nameFilter != "" { - pathParams = fmt.Sprintf("/%s", nameFilter) - } - queryParams := urlQuery(map[string]string{ - "type": typeFilter, - }) - - return fmt.Sprintf("https://%s/v1/datalabels%s%s", c.ControlPlane, pathParams, queryParams) - }, - NewResponseData: func(d *schema.ResourceData) ResponseData { - nameFilter := d.Get("name").(string) - if nameFilter == "" { - return &GetDataLabelsResponse{} - } else { - return &GetDataLabelResponse{} - } - }, - } -} - -func dataSourceDatalabel() *schema.Resource { +func dataSourceSchema() *schema.Resource { return &schema.Resource{ Description: "Retrieve and filter data labels. See also resource [`cyral_datalabel`](../resources/datalabel.md).", - ReadContext: ReadResource(dataSourceDatalabelReadConfig()), + ReadContext: core.ReadResource(readConfig()), Schema: map[string]*schema.Schema{ "name": { Description: "Retrieve the unique label with this name, if it exists.", @@ -90,11 +24,11 @@ func dataSourceDatalabel() *schema.Resource { Optional: true, }, "type": { - Description: fmt.Sprintf("Filter the results by type of data label. Defaults to `%s`, which will return all label types. The labels you create will always have type `CUSTOM`. Labels that come pre-configured in the control plane have type `PREDEFINED`. List of supported types:", defaultDataLabelType) + supportedTypesMarkdown(dataLabelTypes()), + Description: fmt.Sprintf("Filter the results by type of data label. Defaults to `%s`, which will return all label types. The labels you create will always have type `CUSTOM`. Labels that come pre-configured in the control plane have type `PREDEFINED`. List of supported types:", Default) + utils.SupportedValuesAsMarkdown(TypesAsString()), Type: schema.TypeString, Optional: true, - Default: defaultDataLabelType, - ValidateFunc: validation.StringInSlice(append(dataLabelTypes(), ""), false), + Default: Default, + ValidateFunc: validation.StringInSlice(append(TypesAsString(), ""), false), }, "datalabel_list": { Description: "List of existing data labels satisfying the filter criteria.", @@ -160,3 +94,75 @@ func dataSourceDatalabel() *schema.Resource { }, } } + +func readConfig() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ + Name: "DatalabelDataSourceRead", + HttpMethod: http.MethodGet, + CreateURL: func(d *schema.ResourceData, c *client.Client) string { + nameFilter := d.Get("name").(string) + typeFilter := d.Get("type").(string) + var pathParams string + if nameFilter != "" { + pathParams = fmt.Sprintf("/%s", nameFilter) + } + queryParams := utils.UrlQuery(map[string]string{ + "type": typeFilter, + }) + + return fmt.Sprintf("https://%s/v1/datalabels%s%s", c.ControlPlane, pathParams, queryParams) + }, + NewResponseData: func(d *schema.ResourceData) core.ResponseData { + nameFilter := d.Get("name").(string) + if nameFilter == "" { + return &GetDataLabelsResponse{} + } else { + return &GetDataLabelResponse{} + } + }, + } +} + +// Due to the fact that this API will return different payloads if name filters are provider or +// not, we need to support two different return payloads. This should be eliminated in the v2 +// of this API. +type GetDataLabelResponse DataLabel + +func (resp *GetDataLabelResponse) WriteToSchema(d *schema.ResourceData) error { + if err := writeDataLabelsToDataSourceSchema([]*DataLabel{(*DataLabel)(resp)}, d); err != nil { + return err + } + d.SetId(uuid.New().String()) + return nil +} + +type GetDataLabelsResponse struct { + Labels []*DataLabel `json:"labels"` +} + +func (dl *GetDataLabelsResponse) WriteToSchema(d *schema.ResourceData) error { + if err := writeDataLabelsToDataSourceSchema(dl.Labels, d); err != nil { + return err + } + + d.SetId(uuid.New().String()) + return nil +} + +func writeDataLabelsToDataSourceSchema(labels []*DataLabel, d *schema.ResourceData) error { + var labelsList []interface{} + for _, label := range labels { + labelsList = append(labelsList, map[string]interface{}{ + "name": label.Name, + "description": label.Description, + "type": label.Type, + "tags": label.Tags.AsInterface(), + "classification_rule": label.ClassificationRule.AsInterface(), + "implicit": label.Implicit, + }) + } + if err := d.Set("datalabel_list", labelsList); err != nil { + return err + } + return nil +} diff --git a/cyral/data_source_cyral_datalabel_test.go b/cyral/internal/datalabel/datasource_test.go similarity index 65% rename from cyral/data_source_cyral_datalabel_test.go rename to cyral/internal/datalabel/datasource_test.go index a86119f6..9fdfcd2e 100644 --- a/cyral/data_source_cyral_datalabel_test.go +++ b/cyral/internal/datalabel/datasource_test.go @@ -1,27 +1,31 @@ -package cyral +package datalabel_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/datalabel" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const ( datalabelDataSourceName = "data-datalabel" ) -func datalabelDataSourceTestDataLabels() []*DataLabel { - return []*DataLabel{ +func datalabelDataSourceTestDataLabels() []*datalabel.DataLabel { + return []*datalabel.DataLabel{ { - Name: accTestName(datalabelDataSourceName, "1"), - Type: dataLabelTypeCustom, + Name: utils.AccTestName(datalabelDataSourceName, "1"), + Type: datalabel.Custom, Description: "description-1", Tags: []string{"tag-1", "tag-2"}, }, { - Name: accTestName(datalabelDataSourceName, "2"), - Type: dataLabelTypeCustom, + Name: utils.AccTestName(datalabelDataSourceName, "2"), + Type: datalabel.Custom, Description: "description-2", Tags: []string{"tag-3"}, }, @@ -36,12 +40,16 @@ func TestAccDatalabelDataSource(t *testing.T) { testConfigNameFilter2, testFuncNameFilter2 := testDatalabelDataSource(t, "name_filter_2", dataLabels, dataLabels[1].Name, "") testConfigTypeFilterPredefined, testFuncTypeFilterPredefined := testDatalabelDataSource(t, - "type_filter_predefined", dataLabels, "", dataLabelTypePredefined) + "type_filter_predefined", dataLabels, "", string(datalabel.Predefined)) testConfigTypeFilterCustom, testFuncTypeFilterCustom := testDatalabelDataSource(t, - "type_filter_custom", dataLabels, "", dataLabelTypeCustom) + "type_filter_custom", dataLabels, "", string(datalabel.Custom)) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: testConfigNameFilter1, @@ -66,7 +74,7 @@ func TestAccDatalabelDataSource(t *testing.T) { func testDatalabelDataSource( t *testing.T, dsourceName string, - dataLabels []*DataLabel, + dataLabels []*datalabel.DataLabel, nameFilter, typeFilter string, ) ( string, resource.TestCheckFunc, @@ -77,16 +85,22 @@ func testDatalabelDataSource( func testDatalabelDataSourceConfig( dsourceName string, - dataLabels []*DataLabel, - nameFilter, - typeFilter string, + dataLabels []*datalabel.DataLabel, + nameFilter, typeFilter string, ) string { var config string var dependsOn []string for i, dataLabel := range dataLabels { + ruleType, ruleCode, ruleStatus := "", "", "" + if dataLabel.ClassificationRule != nil { + ruleType = dataLabel.ClassificationRule.RuleType + ruleCode = dataLabel.ClassificationRule.RuleCode + ruleStatus = dataLabel.ClassificationRule.RuleStatus + } resName := fmt.Sprintf("test_datalabel_%d", i) - config += formatDataLabelIntoConfig(resName, dataLabel) - dependsOn = append(dependsOn, datalabelConfigResourceFullName(resName)) + config += utils.FormatDataLabelIntoConfig(resName, dataLabel.Name, dataLabel.Description, + ruleType, ruleCode, ruleStatus, dataLabel.Tags) + dependsOn = append(dependsOn, utils.DatalabelConfigResourceFullName(resName)) } config += datalabelDataSourceConfig(dsourceName, nameFilter, typeFilter, dependsOn) @@ -96,7 +110,7 @@ func testDatalabelDataSourceConfig( func testDatalabelDataSourceChecks( t *testing.T, dsourceName string, - dataLabels []*DataLabel, + dataLabels []*datalabel.DataLabel, nameFilter, typeFilter string, ) resource.TestCheckFunc { dataSourceFullName := fmt.Sprintf("data.cyral_datalabel.%s", dsourceName) @@ -105,9 +119,9 @@ func testDatalabelDataSourceChecks( return resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr(dataSourceFullName, "datalabel_list.#", - notZeroRegex(), + utils.NotZeroRegex(), ), - dsourceCheckTypeFilter( + utils.DSourceCheckTypeFilter( dataSourceFullName, "datalabel_list.%d.type", typeFilter, @@ -141,11 +155,11 @@ func testDatalabelDataSourceChecks( return resource.ComposeTestCheckFunc(checkFuncs...) } -func filterDataLabels(dataLabels []*DataLabel, nameFilter, typeFilter string) []*DataLabel { - var filteredDataLabels []*DataLabel +func filterDataLabels(dataLabels []*datalabel.DataLabel, nameFilter, typeFilter string) []*datalabel.DataLabel { + var filteredDataLabels []*datalabel.DataLabel for _, dataLabel := range dataLabels { if (nameFilter == "" || dataLabel.Name == nameFilter) && - (typeFilter == "" || dataLabel.Type == typeFilter) { + (typeFilter == "" || string(dataLabel.Type) == typeFilter) { filteredDataLabels = append(filteredDataLabels, dataLabel) } } @@ -158,5 +172,5 @@ func datalabelDataSourceConfig(dsourceName, nameFilter, typeFilter string, depen name = "%s" type = "%s" depends_on = %s - }`, dsourceName, nameFilter, typeFilter, listToStr(dependsOn)) + }`, dsourceName, nameFilter, typeFilter, utils.ListToStr(dependsOn)) } diff --git a/cyral/internal/datalabel/model.go b/cyral/internal/datalabel/model.go new file mode 100644 index 00000000..55bf6462 --- /dev/null +++ b/cyral/internal/datalabel/model.go @@ -0,0 +1,71 @@ +package datalabel + +import ( + "fmt" + + cs "github.com/cyralinc/terraform-provider-cyral/cyral/internal/datalabel/classificationrule" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +type Tags []string + +func (dlt Tags) AsInterface() []interface{} { + var tagIfaces []interface{} + for _, tag := range dlt { + tagIfaces = append(tagIfaces, tag) + } + return tagIfaces +} + +type DataLabel struct { + Name string `json:"name,omitempty"` + Type Type `json:"type,omitempty"` + Description string `json:"description,omitempty"` + Tags Tags `json:"tags,omitempty"` + ClassificationRule *cs.ClassificationRule `json:"classificationRule,omitempty"` + Implicit bool `json:"implicit,omitempty"` +} + +func (dl *DataLabel) WriteToSchema(d *schema.ResourceData) error { + if err := d.Set("description", dl.Description); err != nil { + return fmt.Errorf("error setting 'description' field: %w", err) + } + + if err := d.Set("tags", dl.Tags.AsInterface()); err != nil { + return fmt.Errorf("error setting 'tags' field: %w", err) + } + + if err := d.Set("classification_rule", dl.ClassificationRule.AsInterface()); err != nil { + return fmt.Errorf("error setting 'classification_rule' field: %w", err) + } + + d.SetId(dl.Name) + + return nil +} + +func (dl *DataLabel) ReadFromSchema(d *schema.ResourceData) error { + var tags []string + tagIfaces := d.Get("tags").([]any) + for _, tagIface := range tagIfaces { + tags = append(tags, tagIface.(string)) + } + + var classificationRule *cs.ClassificationRule + classificationRuleList := d.Get("classification_rule").(*schema.Set).List() + if len(classificationRuleList) > 0 { + classificationRuleMap := classificationRuleList[0].(map[string]any) + classificationRule = &cs.ClassificationRule{ + RuleType: classificationRuleMap["rule_type"].(string), + RuleCode: classificationRuleMap["rule_code"].(string), + RuleStatus: classificationRuleMap["rule_status"].(string), + } + } + dl.Name = d.Get("name").(string) + dl.Type = Custom + dl.Description = d.Get("description").(string) + dl.Tags = tags + dl.ClassificationRule = classificationRule + + return nil +} diff --git a/cyral/internal/datalabel/resource.go b/cyral/internal/datalabel/resource.go new file mode 100644 index 00000000..4ebd39d8 --- /dev/null +++ b/cyral/internal/datalabel/resource.go @@ -0,0 +1,134 @@ +package datalabel + +import ( + "context" + "fmt" + "net/http" + + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/datalabel/classificationrule" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceSchema() *schema.Resource { + return &schema.Resource{ + Description: "Manages data labels. Data labels are part of the Cyral [Data Map](https://cyral.com/docs/policy/datamap).", + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ + Name: "DataLabelResourceCreate", + HttpMethod: http.MethodPut, + CreateURL: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/datalabels/%s", + c.ControlPlane, + d.Get("name").(string)) + }, + NewResourceData: func() core.ResourceData { return &DataLabel{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &DataLabel{} }, + }, readDataLabelConfig, + ), + ReadContext: core.ReadResource(readDataLabelConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ + Name: "DataLabelResourceUpdate", + HttpMethod: http.MethodPut, + CreateURL: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/datalabels/%s", + c.ControlPlane, + d.Get("name").(string)) + }, + NewResourceData: func() core.ResourceData { return &DataLabel{} }, + }, readDataLabelConfig, + ), + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ + Name: "DataLabelResourceDelete", + HttpMethod: http.MethodDelete, + CreateURL: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/datalabels/%s", + c.ControlPlane, + d.Get("name").(string)) + }, + }, + ), + Schema: map[string]*schema.Schema{ + "name": { + Description: "Name of the data label.", + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "description": { + Description: "Description of the data label.", + Type: schema.TypeString, + Optional: true, + }, + "tags": { + Description: "Tags that can be used to categorize data labels.", + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "classification_rule": { + Description: "Classification rules are used by the " + + "[Automatic Data Map](https://cyral.com/docs/policy/automatic-datamap) feature to automatically map " + + "data locations to labels.", + Optional: true, + Type: schema.TypeSet, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "rule_type": { + Description: "Type of the classification rule. List of supported values: " + + utils.SupportedValuesAsMarkdown(classificationrule.TypesAsString()), + Type: schema.TypeString, + Optional: true, + Default: classificationrule.Unknown, + ValidateFunc: validation.StringInSlice(classificationrule.TypesAsString(), false), + }, + "rule_code": { + Description: "Actual code of the classification rule. For example, this attribute may contain " + + "REGO code for `REGO`-type classification rules.", + Type: schema.TypeString, + Optional: true, + }, + "rule_status": { + Description: "Status of the classification rule. List of supported values: " + + utils.SupportedValuesAsMarkdown(classificationrule.StatusesAsString()), + Type: schema.TypeString, + Optional: true, + Default: classificationrule.Enabled, + ValidateFunc: validation.StringInSlice(classificationrule.StatusesAsString(), false), + }, + }, + }, + }, + }, + Importer: &schema.ResourceImporter{ + StateContext: func( + ctx context.Context, + d *schema.ResourceData, + m interface{}, + ) ([]*schema.ResourceData, error) { + d.Set("name", d.Id()) + return []*schema.ResourceData{d}, nil + }, + }, + } +} + +var readDataLabelConfig = core.ResourceOperationConfig{ + Name: "DataLabelResourceRead", + HttpMethod: http.MethodGet, + CreateURL: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/datalabels/%s", + c.ControlPlane, + d.Get("name").(string)) + }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &DataLabel{} }, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Data Label"}, +} diff --git a/cyral/resource_cyral_datalabel_test.go b/cyral/internal/datalabel/resource_test.go similarity index 57% rename from cyral/resource_cyral_datalabel_test.go rename to cyral/internal/datalabel/resource_test.go index 99a1a7cb..719b6abf 100644 --- a/cyral/resource_cyral_datalabel_test.go +++ b/cyral/internal/datalabel/resource_test.go @@ -1,22 +1,26 @@ -package cyral +package datalabel_test import ( - "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/datalabel" + cs "github.com/cyralinc/terraform-provider-cyral/cyral/internal/datalabel/classificationrule" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const ( datalabelResourceName = "datalabel" ) -func initialDataLabelConfig() *DataLabel { - return &DataLabel{ - Name: accTestName(datalabelResourceName, "label1"), +func initialDataLabelConfig() *datalabel.DataLabel { + return &datalabel.DataLabel{ + Name: utils.AccTestName(datalabelResourceName, "label1"), Description: "label1-description", Tags: []string{"tag1", "tag2"}, - ClassificationRule: &DataLabelClassificationRule{ + ClassificationRule: &cs.ClassificationRule{ RuleType: "UNKNOWN", RuleCode: "", RuleStatus: "ENABLED", @@ -24,12 +28,12 @@ func initialDataLabelConfig() *DataLabel { } } -func updatedDataLabelConfig() *DataLabel { - return &DataLabel{ - Name: accTestName(datalabelResourceName, "label2"), +func updatedDataLabelConfig() *datalabel.DataLabel { + return &datalabel.DataLabel{ + Name: utils.AccTestName(datalabelResourceName, "label2"), Description: "label2-description", Tags: []string{"tag1", "tag2"}, - ClassificationRule: &DataLabelClassificationRule{ + ClassificationRule: &cs.ClassificationRule{ RuleType: "REGO", RuleCode: "int main() {cout << 'Hello World' << endl; return 0;}", RuleStatus: "DISABLED", @@ -42,9 +46,12 @@ func TestAccDatalabelResource(t *testing.T) { "main_test", initialDataLabelConfig()) testUpdatedConfig, testUpdatedFunc := setupDatalabelTest(t, "main_test", updatedDataLabelConfig()) - resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: testInitialConfig, @@ -63,10 +70,17 @@ func TestAccDatalabelResource(t *testing.T) { }) } -func setupDatalabelTest(t *testing.T, resName string, dataLabel *DataLabel) (string, resource.TestCheckFunc) { - configuration := formatDataLabelIntoConfig(resName, dataLabel) +func setupDatalabelTest(t *testing.T, resName string, dataLabel *datalabel.DataLabel) (string, resource.TestCheckFunc) { + ruleType, ruleCode, ruleStatus := "", "", "" + if dataLabel.ClassificationRule != nil { + ruleType = dataLabel.ClassificationRule.RuleType + ruleCode = dataLabel.ClassificationRule.RuleCode + ruleStatus = dataLabel.ClassificationRule.RuleStatus + } + config := utils.FormatDataLabelIntoConfig(resName, dataLabel.Name, dataLabel.Description, + ruleType, ruleCode, ruleStatus, dataLabel.Tags) - resourceFullName := datalabelConfigResourceFullName(resName) + resourceFullName := utils.DatalabelConfigResourceFullName(resName) testFunction := resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(resourceFullName, "name", dataLabel.Name), @@ -89,38 +103,5 @@ func setupDatalabelTest(t *testing.T, resName string, dataLabel *DataLabel) (str ), ) - return configuration, testFunction -} - -func datalabelConfigResourceFullName(resName string) string { - return fmt.Sprintf("cyral_datalabel.%s", resName) -} - -func formatDataLabelIntoConfig(resName string, dataLabel *DataLabel) string { - var classificationRuleConfig string - if dataLabel.ClassificationRule != nil { - classificationRuleConfig = fmt.Sprintf(` - classification_rule { - rule_type = "%s" - rule_code = "%s" - rule_status = "%s" - }`, - dataLabel.ClassificationRule.RuleType, - dataLabel.ClassificationRule.RuleCode, - dataLabel.ClassificationRule.RuleStatus, - ) - } - return fmt.Sprintf(` - resource "cyral_datalabel" "%s" { - name = "%s" - description = "%s" - tags = %s - %s - }`, - resName, - dataLabel.Name, - dataLabel.Description, - listToStr(dataLabel.Tags), - classificationRuleConfig, - ) + return config, testFunction } diff --git a/cyral/internal/datalabel/schema_loader.go b/cyral/internal/datalabel/schema_loader.go new file mode 100644 index 00000000..cf959876 --- /dev/null +++ b/cyral/internal/datalabel/schema_loader.go @@ -0,0 +1,31 @@ +package datalabel + +import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/core" +) + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "datalabel" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: "cyral_datalabel", + Type: core.DataSourceSchemaType, + Schema: dataSourceSchema, + }, + { + Name: "cyral_datalabel", + Type: core.ResourceSchemaType, + Schema: resourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/data_source_cyral_integration_idp.go b/cyral/internal/deprecated/data_source_cyral_integration_idp.go similarity index 81% rename from cyral/data_source_cyral_integration_idp.go rename to cyral/internal/deprecated/data_source_cyral_integration_idp.go index a6fbfdc3..e571301a 100644 --- a/cyral/data_source_cyral_integration_idp.go +++ b/cyral/internal/deprecated/data_source_cyral_integration_idp.go @@ -1,4 +1,4 @@ -package cyral +package deprecated import ( "context" @@ -8,14 +8,15 @@ import ( "net/http" "sort" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) -func dataSourceIntegrationIdP() *schema.Resource { +func DataSourceIntegrationIdP() *schema.Resource { return &schema.Resource{ Description: "Retrieve and filter IdP integrations.", ReadContext: dataSourceIntegrationIdPRead, @@ -90,12 +91,12 @@ func dataSourceIntegrationIdPRead( body, err := c.DoRequest(url, http.MethodGet, nil) if err != nil { - return createError("Unable to execute request to read idp integrations", err.Error()) + return utils.CreateError("Unable to execute request to read idp integrations", err.Error()) } var idpIntegrations = IdPIntegrations{} if err := json.Unmarshal(body, &idpIntegrations); err != nil { - return createError("Unable to unmarshal idp integrations", err.Error()) + return utils.CreateError("Unable to unmarshal idp integrations", err.Error()) } log.Printf("[DEBUG] Response body (unmarshalled): %#v", idpIntegrations) @@ -134,3 +135,21 @@ func dataSourceIntegrationIdPRead( return nil } + +func ListIdPIntegrations(c *client.Client) (*IdPIntegrations, error) { + log.Printf("[DEBUG] Init ListIdPIntegrations") + + url := fmt.Sprintf("https://%s/v1/integrations/saml", c.ControlPlane) + body, err := c.DoRequest(url, http.MethodGet, nil) + if err != nil { + return nil, err + } + resp := &IdPIntegrations{} + if err := json.Unmarshal(body, resp); err != nil { + return nil, err + } + log.Printf("[DEBUG] Response body (unmarshalled): %#v", resp) + log.Printf("[DEBUG] End ListIdPIntegrations") + + return resp, nil +} diff --git a/cyral/data_source_cyral_integration_idp_test.go b/cyral/internal/deprecated/data_source_cyral_integration_idp_test.go similarity index 64% rename from cyral/data_source_cyral_integration_idp_test.go rename to cyral/internal/deprecated/data_source_cyral_integration_idp_test.go index 2c0286de..ce18db03 100644 --- a/cyral/data_source_cyral_integration_idp_test.go +++ b/cyral/internal/deprecated/data_source_cyral_integration_idp_test.go @@ -1,3 +1,3 @@ -package cyral +package deprecated_test // TODO: ACC tests -aholmquist 2022-08-29 diff --git a/cyral/data_source_cyral_sidecar_cft_template.go b/cyral/internal/deprecated/data_source_cyral_sidecar_cft_template.go similarity index 91% rename from cyral/data_source_cyral_sidecar_cft_template.go rename to cyral/internal/deprecated/data_source_cyral_sidecar_cft_template.go index 4f24e002..f1c94986 100644 --- a/cyral/data_source_cyral_sidecar_cft_template.go +++ b/cyral/internal/deprecated/data_source_cyral_sidecar_cft_template.go @@ -1,4 +1,4 @@ -package cyral +package deprecated import ( "encoding/json" @@ -8,13 +8,15 @@ import ( "strconv" "strings" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const CloudFormationDeploymentMethod = "cft-ec2" -func dataSourceSidecarCftTemplate() *schema.Resource { +func DataSourceSidecarCftTemplate() *schema.Resource { return &schema.Resource{ DeprecationMessage: "This data source was deprecated. It will be removed in the next major version of " + "the provider.", @@ -113,17 +115,17 @@ func removePortFromURL(url string) string { return strings.Split(url, ":")[0] } -func getSidecarData(c *client.Client, d *schema.ResourceData) (SidecarData, error) { +func getSidecarData(c *client.Client, d *schema.ResourceData) (sidecar.SidecarData, error) { url := fmt.Sprintf("https://%s/v1/sidecars/%s", c.ControlPlane, d.Get("sidecar_id").(string)) body, err := c.DoRequest(url, http.MethodGet, nil) if err != nil { - return SidecarData{}, err + return sidecar.SidecarData{}, err } - response := SidecarData{} + response := sidecar.SidecarData{} if err := json.Unmarshal(body, &response); err != nil { - return SidecarData{}, err + return sidecar.SidecarData{}, err } return response, nil @@ -171,7 +173,7 @@ func filterIntegrationData(integrations []IntegrationsData, id string) *Integrat } func getTemplateForSidecarProperties( - sidecarData SidecarData, + sidecarData sidecar.SidecarData, logging []IntegrationsData, metrics []IntegrationsData, c *client.Client, @@ -230,7 +232,7 @@ func getTemplateForSidecarProperties( properties := sidecarData.SidecarProperties if properties != nil && properties.DeploymentMethod == CloudFormationDeploymentMethod { url = fmt.Sprintf("https://%s/deploy/cft/", controlPlane) - url += urlQuery(sidecarTemplatePropertiesKV) + url += utils.UrlQuery(sidecarTemplatePropertiesKV) } else { return nil, fmt.Errorf("invalid deployment method, only '%s' is supported", CloudFormationDeploymentMethod) diff --git a/cyral/data_source_cyral_sidecar_cft_template_test.go b/cyral/internal/deprecated/data_source_cyral_sidecar_cft_template_test.go similarity index 64% rename from cyral/data_source_cyral_sidecar_cft_template_test.go rename to cyral/internal/deprecated/data_source_cyral_sidecar_cft_template_test.go index 26ea4f7f..16f1a441 100644 --- a/cyral/data_source_cyral_sidecar_cft_template_test.go +++ b/cyral/internal/deprecated/data_source_cyral_sidecar_cft_template_test.go @@ -1,11 +1,14 @@ -package cyral +package deprecated_test import ( "fmt" "regexp" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const ( @@ -17,7 +20,11 @@ func TestAccSidecarCftTemplateDataSource(t *testing.T) { resource.ParallelTest( t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: cftConfig, @@ -30,26 +37,22 @@ func TestAccSidecarCftTemplateDataSource(t *testing.T) { func setupSidecarCftTemplateTest() (string, resource.TestCheckFunc) { var configuration string - configuration += formatBasicSidecarIntoConfig( - basicSidecarResName, - accTestName(sidecarCftTemplateDataSourceName, "sidecar"), + configuration += utils.FormatBasicSidecarIntoConfig( + utils.BasicSidecarResName, + utils.AccTestName(sidecarCftTemplateDataSourceName, "sidecar"), "cft-ec2", "", ) - configuration += formatELKIntegrationDataIntoConfig( - ELKIntegration{ - Name: accTestName(sidecarCftTemplateDataSourceName, "elk"), - KibanaURL: "kibana.local", - ESURL: "es.local", - }, + configuration += utils.FormatELKIntegrationDataIntoConfig( + utils.AccTestName(sidecarCftTemplateDataSourceName, "elk"), + "kibana.local", + "es.local", ) - configuration += formatDatadogIntegrationDataIntoConfig( - DatadogIntegration{ - Name: accTestName(sidecarCftTemplateDataSourceName, "datadog"), - APIKey: "datadog-api-key", - }, + configuration += utils.FormatDatadogIntegrationDataIntoConfig( + utils.AccTestName(sidecarCftTemplateDataSourceName, "datadog"), + "datadog-api-key", ) configuration += formatSidecarCftTemplateDataIntoConfig( - basicSidecarID, + utils.BasicSidecarID, "cyral_integration_elk.elk_integration.id", "cyral_integration_datadog.datadog_integration.id", true, diff --git a/cyral/data_source_cyral_sidecar_instance_ids.go b/cyral/internal/deprecated/data_source_cyral_sidecar_instance_ids.go similarity index 86% rename from cyral/data_source_cyral_sidecar_instance_ids.go rename to cyral/internal/deprecated/data_source_cyral_sidecar_instance_ids.go index 81d69b7d..50b7e581 100644 --- a/cyral/data_source_cyral_sidecar_instance_ids.go +++ b/cyral/internal/deprecated/data_source_cyral_sidecar_instance_ids.go @@ -1,4 +1,4 @@ -package cyral +package deprecated import ( "context" @@ -8,7 +8,8 @@ import ( "net/http" "sort" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -22,7 +23,7 @@ type DeprecatedSidecarInstances struct { ASGInstanceID string `json:"asg_instance,omitempty"` } -func dataSourceSidecarInstanceIDs() *schema.Resource { +func DataSourceSidecarInstanceIDs() *schema.Resource { return &schema.Resource{ DeprecationMessage: "This data source was deprecated. It will be removed in the next major version of " + "the provider. Use the data source `cyral_sidecar_instance` instead", @@ -64,7 +65,7 @@ func dataSourceSidecarInstanceIDsRead( sidecarID := d.Get("sidecar_id").(string) sidecarDetails, err := getSidecarDetails(c, sidecarID) if err != nil { - return createError(fmt.Sprintf("Unable to retrieve sidecar details. SidecarID: %s", + return utils.CreateError(fmt.Sprintf("Unable to retrieve sidecar details. SidecarID: %s", sidecarID), err.Error()) } @@ -85,9 +86,7 @@ func dataSourceSidecarInstanceIDsRead( func getSidecarDetails(c *client.Client, sidecarID string) (SidecarDetails, error) { log.Printf("[DEBUG] Init getSidecarDetails") - // Remove port from control plane to make request to Jeeves server - controlPlaneWithoutPort := removePortFromURL(c.ControlPlane) - url := fmt.Sprintf("https://%s/sidecars/%s/details", controlPlaneWithoutPort, sidecarID) + url := fmt.Sprintf("https://%s/sidecars/%s/details", c.ControlPlane, sidecarID) body, err := c.DoRequest(url, http.MethodGet, nil) if err != nil { return SidecarDetails{}, err diff --git a/cyral/data_source_cyral_sidecar_instance_ids_test.go b/cyral/internal/deprecated/data_source_cyral_sidecar_instance_ids_test.go similarity index 77% rename from cyral/data_source_cyral_sidecar_instance_ids_test.go rename to cyral/internal/deprecated/data_source_cyral_sidecar_instance_ids_test.go index c5d16905..681ef0f6 100644 --- a/cyral/data_source_cyral_sidecar_instance_ids_test.go +++ b/cyral/internal/deprecated/data_source_cyral_sidecar_instance_ids_test.go @@ -1,11 +1,14 @@ -package cyral +package deprecated_test import ( "fmt" "regexp" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const ( @@ -17,7 +20,11 @@ func TestAccSidecarInstanceIDsDataSource(t *testing.T) { resource.ParallelTest( t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: testAccSidecarInstanceIDsConfig_EmptySidecarID(), @@ -66,9 +73,9 @@ func testAccSidecarInstanceIDsConfig_NoSidecarInstances() string { // Creates a sidecar that doesn't have any instances, since it was not // deployed. var config string - config += formatBasicSidecarIntoConfig( - basicSidecarResName, - accTestName(sidecarInstanceIDsDataSourceName, "sidecar"), + config += utils.FormatBasicSidecarIntoConfig( + utils.BasicSidecarResName, + utils.AccTestName(sidecarInstanceIDsDataSourceName, "sidecar"), "cft-ec2", "", ) @@ -76,7 +83,7 @@ func testAccSidecarInstanceIDsConfig_NoSidecarInstances() string { ` data "cyral_sidecar_instance_ids" "instance_ids" { sidecar_id = %s - }`, basicSidecarID, + }`, utils.BasicSidecarID, ) return config } diff --git a/cyral/integrations_data.go b/cyral/internal/deprecated/integrations_data.go similarity index 98% rename from cyral/integrations_data.go rename to cyral/internal/deprecated/integrations_data.go index 5d4d397f..14ef1a0b 100644 --- a/cyral/integrations_data.go +++ b/cyral/internal/deprecated/integrations_data.go @@ -1,4 +1,4 @@ -package cyral +package deprecated import ( "encoding/json" diff --git a/cyral/integrations_data_test.go b/cyral/internal/deprecated/integrations_data_test.go similarity index 72% rename from cyral/integrations_data_test.go rename to cyral/internal/deprecated/integrations_data_test.go index b12755fb..0a6a4b34 100644 --- a/cyral/integrations_data_test.go +++ b/cyral/internal/deprecated/integrations_data_test.go @@ -1,19 +1,20 @@ -package cyral +package deprecated_test import ( "encoding/json" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/deprecated" "github.com/stretchr/testify/require" ) -func sampleSplunkIntegrationsData() *IntegrationsData { - return &IntegrationsData{ +func sampleSplunkIntegrationsData() *deprecated.IntegrationsData { + return &deprecated.IntegrationsData{ Id: "id1", Type: "splunk", Name: "name1", Label: "label1", - Value: SplunkIntegration{ + Value: deprecated.SplunkIntegration{ Name: "name1", AccessToken: "accessToken1", Port: 0, @@ -26,8 +27,8 @@ func sampleSplunkIntegrationsData() *IntegrationsData { } func TestIntegrationsData_GetValue_Default(t *testing.T) { - integrationsData := NewDefaultIntegrationsData() - expected := NewDefaultIntegrationsData().Value.(string) + integrationsData := deprecated.NewDefaultIntegrationsData() + expected := deprecated.NewDefaultIntegrationsData().Value.(string) actual, err := integrationsData.GetValue() require.NoError(t, err) require.Equal(t, expected, actual) @@ -36,7 +37,7 @@ func TestIntegrationsData_GetValue_Default(t *testing.T) { func TestIntegrationsData_GetValue_Splunk(t *testing.T) { splunkIntegrationsData := sampleSplunkIntegrationsData() - expectedBytes, err := json.Marshal(SplunkIntegration{ + expectedBytes, err := json.Marshal(deprecated.SplunkIntegration{ Name: "name1", AccessToken: "accessToken1", Port: 0, diff --git a/cyral/model_integration_idp.go b/cyral/internal/deprecated/model_integration_idp.go similarity index 99% rename from cyral/model_integration_idp.go rename to cyral/internal/deprecated/model_integration_idp.go index 57cda94d..cd0f17d2 100644 --- a/cyral/model_integration_idp.go +++ b/cyral/internal/deprecated/model_integration_idp.go @@ -1,4 +1,4 @@ -package cyral +package deprecated type IdPIntegrations struct { Connections *Connections `json:"connections,omitempty"` diff --git a/cyral/resource_cyral_integration_datadog.go b/cyral/internal/deprecated/resource_cyral_integration_datadog.go similarity index 71% rename from cyral/resource_cyral_integration_datadog.go rename to cyral/internal/deprecated/resource_cyral_integration_datadog.go index 75e7b089..c4229d44 100644 --- a/cyral/resource_cyral_integration_datadog.go +++ b/cyral/internal/deprecated/resource_cyral_integration_datadog.go @@ -1,10 +1,11 @@ -package cyral +package deprecated import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -27,45 +28,45 @@ func (data *DatadogIntegration) ReadFromSchema(d *schema.ResourceData) error { return nil } -var ReadDatadogConfig = ResourceOperationConfig{ +var ReadDatadogConfig = core.ResourceOperationConfig{ Name: "DatadogResourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/datadog/%s", c.ControlPlane, d.Id()) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &DatadogIntegration{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "Integration datadog"}, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &DatadogIntegration{} }, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Integration datadog"}, } -func resourceIntegrationDatadog() *schema.Resource { +func ResourceIntegrationDatadog() *schema.Resource { return &schema.Resource{ DeprecationMessage: "If configuring Datadog for logging purposes, use resource `cyral_integration_logging` instead.", Description: "Manages [integration with DataDog](https://cyral.com/docs/integrations/apm/datadog/) " + "to push sidecar logs and/or metrics.", - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "DatadogResourceCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/datadog", c.ControlPlane) }, - NewResourceData: func() ResourceData { return &DatadogIntegration{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &IDBasedResponse{} }, + NewResourceData: func() core.ResourceData { return &DatadogIntegration{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &core.IDBasedResponse{} }, }, ReadDatadogConfig, ), - ReadContext: ReadResource(ReadDatadogConfig), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadDatadogConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "DatadogResourceUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/datadog/%s", c.ControlPlane, d.Id()) }, - NewResourceData: func() ResourceData { return &DatadogIntegration{} }, + NewResourceData: func() core.ResourceData { return &DatadogIntegration{} }, }, ReadDatadogConfig, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "DatadogResourceDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { diff --git a/cyral/resource_cyral_integration_datadog_test.go b/cyral/internal/deprecated/resource_cyral_integration_datadog_test.go similarity index 51% rename from cyral/resource_cyral_integration_datadog_test.go rename to cyral/internal/deprecated/resource_cyral_integration_datadog_test.go index c327a563..83361419 100644 --- a/cyral/resource_cyral_integration_datadog_test.go +++ b/cyral/internal/deprecated/resource_cyral_integration_datadog_test.go @@ -1,23 +1,26 @@ -package cyral +package deprecated_test import ( - "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/deprecated" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const ( integrationDatadogResourceName = "integration-datadog" ) -var initialDatadogConfig DatadogIntegration = DatadogIntegration{ - Name: accTestName(integrationDatadogResourceName, "datadog"), +var initialDatadogConfig deprecated.DatadogIntegration = deprecated.DatadogIntegration{ + Name: utils.AccTestName(integrationDatadogResourceName, "datadog"), APIKey: "some-api-key", } -var updatedDatadogConfig DatadogIntegration = DatadogIntegration{ - Name: accTestName(integrationDatadogResourceName, "datadog-updated"), +var updatedDatadogConfig deprecated.DatadogIntegration = deprecated.DatadogIntegration{ + Name: utils.AccTestName(integrationDatadogResourceName, "datadog-updated"), APIKey: "some-api-key-updated", } @@ -26,7 +29,11 @@ func TestAccDatadogIntegrationResource(t *testing.T) { testUpdateConfig, testUpdateFunc := setupDatadogTest(updatedDatadogConfig) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: testConfig, @@ -45,22 +52,14 @@ func TestAccDatadogIntegrationResource(t *testing.T) { }) } -func setupDatadogTest(integrationData DatadogIntegration) (string, resource.TestCheckFunc) { - configuration := formatDatadogIntegrationDataIntoConfig(integrationData) +func setupDatadogTest(d deprecated.DatadogIntegration) (string, resource.TestCheckFunc) { + configuration := utils.FormatDatadogIntegrationDataIntoConfig(d.Name, d.APIKey) testFunction := resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("cyral_integration_datadog.datadog_integration", - "name", integrationData.Name), + "name", d.Name), resource.TestCheckResourceAttr("cyral_integration_datadog.datadog_integration", - "api_key", integrationData.APIKey)) + "api_key", d.APIKey)) return configuration, testFunction } - -func formatDatadogIntegrationDataIntoConfig(data DatadogIntegration) string { - return fmt.Sprintf(` - resource "cyral_integration_datadog" "datadog_integration" { - name = "%s" - api_key = "%s" - }`, data.Name, data.APIKey) -} diff --git a/cyral/resource_cyral_integration_elk.go b/cyral/internal/deprecated/resource_cyral_integration_elk.go similarity index 73% rename from cyral/resource_cyral_integration_elk.go rename to cyral/internal/deprecated/resource_cyral_integration_elk.go index e4e7ba55..3ca9b897 100644 --- a/cyral/resource_cyral_integration_elk.go +++ b/cyral/internal/deprecated/resource_cyral_integration_elk.go @@ -1,10 +1,11 @@ -package cyral +package deprecated import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -30,44 +31,44 @@ func (data *ELKIntegration) ReadFromSchema(d *schema.ResourceData) error { return nil } -var ReadELKConfig = ResourceOperationConfig{ +var ReadELKConfig = core.ResourceOperationConfig{ Name: "ELKResourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/elk/%s", c.ControlPlane, d.Id()) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &ELKIntegration{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "Integration elk"}, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &ELKIntegration{} }, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Integration elk"}, } -func resourceIntegrationELK() *schema.Resource { +func ResourceIntegrationELK() *schema.Resource { return &schema.Resource{ DeprecationMessage: "Use resource `cyral_integration_logging` instead.", Description: "Manages [integration with ELK](https://cyral.com/docs/integrations/siem/elk/) to push sidecar metrics.", - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "ELKResourceCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/elk", c.ControlPlane) }, - NewResourceData: func() ResourceData { return &ELKIntegration{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &IDBasedResponse{} }, + NewResourceData: func() core.ResourceData { return &ELKIntegration{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &core.IDBasedResponse{} }, }, ReadELKConfig, ), - ReadContext: ReadResource(ReadELKConfig), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadELKConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "ELKResourceUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/elk/%s", c.ControlPlane, d.Id()) }, - NewResourceData: func() ResourceData { return &ELKIntegration{} }, + NewResourceData: func() core.ResourceData { return &ELKIntegration{} }, }, ReadELKConfig, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "ELKResourceDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { diff --git a/cyral/resource_cyral_integration_elk_test.go b/cyral/internal/deprecated/resource_cyral_integration_elk_test.go similarity index 51% rename from cyral/resource_cyral_integration_elk_test.go rename to cyral/internal/deprecated/resource_cyral_integration_elk_test.go index 05299936..c8c295a0 100644 --- a/cyral/resource_cyral_integration_elk_test.go +++ b/cyral/internal/deprecated/resource_cyral_integration_elk_test.go @@ -1,24 +1,27 @@ -package cyral +package deprecated_test import ( - "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/deprecated" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const ( integrationELKResourceName = "integration-elk" ) -var initialELKConfig ELKIntegration = ELKIntegration{ - Name: accTestName(integrationELKResourceName, "ELK"), +var initialELKConfig deprecated.ELKIntegration = deprecated.ELKIntegration{ + Name: utils.AccTestName(integrationELKResourceName, "ELK"), KibanaURL: "kibana.local", ESURL: "es.local", } -var updatedELKConfig ELKIntegration = ELKIntegration{ - Name: accTestName(integrationELKResourceName, "ELK-updated"), +var updatedELKConfig deprecated.ELKIntegration = deprecated.ELKIntegration{ + Name: utils.AccTestName(integrationELKResourceName, "ELK-updated"), KibanaURL: "kibana-update.local", ESURL: "es-update.local", } @@ -28,7 +31,11 @@ func TestAccELKIntegrationResource(t *testing.T) { testUpdateConfig, testUpdateFunc := setupELKTest(updatedELKConfig) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: testConfig, @@ -47,23 +54,14 @@ func TestAccELKIntegrationResource(t *testing.T) { }) } -func setupELKTest(integrationData ELKIntegration) (string, resource.TestCheckFunc) { - configuration := formatELKIntegrationDataIntoConfig(integrationData) +func setupELKTest(d deprecated.ELKIntegration) (string, resource.TestCheckFunc) { + configuration := utils.FormatELKIntegrationDataIntoConfig(d.Name, d.KibanaURL, d.ESURL) testFunction := resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("cyral_integration_elk.elk_integration", "name", integrationData.Name), - resource.TestCheckResourceAttr("cyral_integration_elk.elk_integration", "kibana_url", integrationData.KibanaURL), - resource.TestCheckResourceAttr("cyral_integration_elk.elk_integration", "es_url", integrationData.ESURL), + resource.TestCheckResourceAttr("cyral_integration_elk.elk_integration", "name", d.Name), + resource.TestCheckResourceAttr("cyral_integration_elk.elk_integration", "kibana_url", d.KibanaURL), + resource.TestCheckResourceAttr("cyral_integration_elk.elk_integration", "es_url", d.ESURL), ) return configuration, testFunction } - -func formatELKIntegrationDataIntoConfig(data ELKIntegration) string { - return fmt.Sprintf(` - resource "cyral_integration_elk" "elk_integration" { - name = "%s" - kibana_url = "%s" - es_url = "%s" - }`, data.Name, data.KibanaURL, data.ESURL) -} diff --git a/cyral/resource_cyral_integration_idp.go b/cyral/internal/deprecated/resource_cyral_integration_idp.go similarity index 93% rename from cyral/resource_cyral_integration_idp.go rename to cyral/internal/deprecated/resource_cyral_integration_idp.go index 50cd991a..d7631c31 100644 --- a/cyral/resource_cyral_integration_idp.go +++ b/cyral/internal/deprecated/resource_cyral_integration_idp.go @@ -1,4 +1,4 @@ -package cyral +package deprecated import ( "context" @@ -6,12 +6,14 @@ import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/idpsaml" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -func resourceIntegrationIdP(identityProvider string, deprecationMessage string) *schema.Resource { +func ResourceIntegrationIdP(identityProvider string, deprecationMessage string) *schema.Resource { return &schema.Resource{ Description: fmt.Sprintf("%v", idpDefaultValues(identityProvider, "resource_description")), CreateContext: resourceIntegrationIdPCreate(identityProvider), @@ -261,40 +263,40 @@ func resourceIntegrationIdP(identityProvider string, deprecationMessage string) func resourceIntegrationIdPCreate(identityProvider string) schema.CreateContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - diag := CreateResource( - ResourceOperationConfig{ + diag := core.CreateResource( + core.ResourceOperationConfig{ Name: "resourceIntegrationIdPCreate - Integration", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/saml", c.ControlPlane) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return &SAMLIntegrationData{ SAMLSetting: &SAMLSetting{ IdentityProvider: identityProvider, }, } }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &AliasBasedResponse{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &AliasBasedResponse{} }, }, readIntegrationIdPConfig, )(ctx, d, m) if !diag.HasError() { - diag = CreateResource( - ResourceOperationConfig{ + diag = core.CreateResource( + core.ResourceOperationConfig{ Name: "resourceIntegrationIdPCreate - IdentityProvider", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/conf/identityProviders/%s", c.ControlPlane, d.Id()) }, - NewResourceData: func() ResourceData { return &IdentityProviderData{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &IdentityProviderData{} }, + NewResourceData: func() core.ResourceData { return &idpsaml.IdentityProviderData{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &idpsaml.IdentityProviderData{} }, }, readIdentityProviderConfig, )(ctx, d, m) if diag.HasError() { // Clean Up Integration IdP - DeleteResource(deleteIntegrationIdPConfig)(ctx, d, m) + core.DeleteResource(deleteIntegrationIdPConfig)(ctx, d, m) } } @@ -303,10 +305,10 @@ func resourceIntegrationIdPCreate(identityProvider string) schema.CreateContextF } func resourceIntegrationIdPRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - diag := ReadResource(readIntegrationIdPConfig)(ctx, d, m) + diag := core.ReadResource(readIntegrationIdPConfig)(ctx, d, m) if !diag.HasError() { - diag = ReadResource(readIdentityProviderConfig)(ctx, d, m) + diag = core.ReadResource(readIdentityProviderConfig)(ctx, d, m) } return diag @@ -314,14 +316,14 @@ func resourceIntegrationIdPRead(ctx context.Context, d *schema.ResourceData, m i func resourceIntegrationIdPUpdate(identityProvider string) schema.UpdateContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - diag := UpdateResource( - ResourceOperationConfig{ + diag := core.UpdateResource( + core.ResourceOperationConfig{ Name: "resourceIntegrationIdPUpdate - Integration", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/saml/%s", c.ControlPlane, d.Id()) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return &SAMLIntegrationData{ SAMLSetting: &SAMLSetting{ IdentityProvider: identityProvider, @@ -336,11 +338,11 @@ func resourceIntegrationIdPUpdate(identityProvider string) schema.UpdateContextF } func resourceIntegrationIdPDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - diag := DeleteResource(deleteIntegrationIdPConfig)(ctx, d, m) + diag := core.DeleteResource(deleteIntegrationIdPConfig)(ctx, d, m) if !diag.HasError() { - diag = DeleteResource( - ResourceOperationConfig{ + diag = core.DeleteResource( + core.ResourceOperationConfig{ Name: "resourceIntegrationIdPDelete - IdentityProvider", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -353,25 +355,25 @@ func resourceIntegrationIdPDelete(ctx context.Context, d *schema.ResourceData, m return diag } -var readIntegrationIdPConfig = ResourceOperationConfig{ +var readIntegrationIdPConfig = core.ResourceOperationConfig{ Name: "resourceIntegrationIdPRead - Integration", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/saml/%s", c.ControlPlane, d.Id()) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &SAMLIntegrationData{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &SAMLIntegrationData{} }, } -var readIdentityProviderConfig = ResourceOperationConfig{ +var readIdentityProviderConfig = core.ResourceOperationConfig{ Name: "resourceIntegrationIdPRead - IdentityProvider", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/conf/identityProviders/%s", c.ControlPlane, d.Id()) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &IdentityProviderData{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &idpsaml.IdentityProviderData{} }, } -var deleteIntegrationIdPConfig = ResourceOperationConfig{ +var deleteIntegrationIdPConfig = core.ResourceOperationConfig{ Name: "resourceIntegrationIdPDelete - Integration", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -564,17 +566,3 @@ func (response AliasBasedResponse) WriteToSchema(d *schema.ResourceData) error { d.SetId(response.Alias) return nil } - -type KeycloakProvider struct{} - -type IdentityProviderData struct { - Keycloak KeycloakProvider `json:"keycloakProvider"` -} - -func (data IdentityProviderData) WriteToSchema(d *schema.ResourceData) error { - return nil -} - -func (data *IdentityProviderData) ReadFromSchema(d *schema.ResourceData) error { - return nil -} diff --git a/cyral/resource_cyral_integration_idp_test.go b/cyral/internal/deprecated/resource_cyral_integration_idp_test.go similarity index 83% rename from cyral/resource_cyral_integration_idp_test.go rename to cyral/internal/deprecated/resource_cyral_integration_idp_test.go index 6951a399..6de0036c 100644 --- a/cyral/resource_cyral_integration_idp_test.go +++ b/cyral/internal/deprecated/resource_cyral_integration_idp_test.go @@ -1,22 +1,25 @@ -package cyral +package deprecated_test import ( "fmt" "regexp" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -const ( - integrationIdPResourceName = "integration-idp" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func TestAccIdPIntegrationResource(t *testing.T) { - idpDisplayName := accTestName(integrationIdPResourceName, "integration") + idpDisplayName := utils.AccTestName(utils.IntegrationIdPResourceName, "integration") resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: testAccIdPIntegrationConfig_EmptySamlp(), @@ -110,7 +113,7 @@ func testAccIdPIntegrationConfig_ADFS_DefaultValues(idpDisplayName string) strin } } } - `, idpDisplayName, testSingleSignOnURL) + `, idpDisplayName, utils.TestSingleSignOnURL) } func testAccIdPIntegrationCheck_ADFS_DefaultValues() resource.TestCheckFunc { @@ -118,7 +121,7 @@ func testAccIdPIntegrationCheck_ADFS_DefaultValues() resource.TestCheckFunc { resource.TestMatchResourceAttr("cyral_integration_idp_adfs.test_idp_integration", "id", regexp.MustCompile(`adfs.`)), resource.TestCheckResourceAttr("cyral_integration_idp_adfs.test_idp_integration", - "samlp.0.config.0.single_sign_on_service_url", testSingleSignOnURL), + "samlp.0.config.0.single_sign_on_service_url", utils.TestSingleSignOnURL), ) } @@ -132,7 +135,7 @@ func testAccIdPIntegrationConfig_AAD_DefaultValues(idpDisplayName string) string } } } - `, idpDisplayName, testSingleSignOnURL) + `, idpDisplayName, utils.TestSingleSignOnURL) } func testAccIdPIntegrationCheck_AAD_DefaultValues() resource.TestCheckFunc { @@ -140,7 +143,7 @@ func testAccIdPIntegrationCheck_AAD_DefaultValues() resource.TestCheckFunc { resource.TestMatchResourceAttr("cyral_integration_idp_aad.test_idp_integration", "id", regexp.MustCompile(`aad.`)), resource.TestCheckResourceAttr("cyral_integration_idp_aad.test_idp_integration", - "samlp.0.config.0.single_sign_on_service_url", testSingleSignOnURL), + "samlp.0.config.0.single_sign_on_service_url", utils.TestSingleSignOnURL), ) } @@ -154,7 +157,7 @@ func testAccIdPIntegrationConfig_Forgerock_DefaultValues(idpDisplayName string) } } } - `, idpDisplayName, testSingleSignOnURL) + `, idpDisplayName, utils.TestSingleSignOnURL) } func testAccIdPIntegrationCheck_Forgerock_DefaultValues() resource.TestCheckFunc { @@ -162,7 +165,7 @@ func testAccIdPIntegrationCheck_Forgerock_DefaultValues() resource.TestCheckFunc resource.TestMatchResourceAttr("cyral_integration_idp_forgerock.test_idp_integration", "id", regexp.MustCompile(`forgerock.`)), resource.TestCheckResourceAttr("cyral_integration_idp_forgerock.test_idp_integration", - "samlp.0.config.0.single_sign_on_service_url", testSingleSignOnURL), + "samlp.0.config.0.single_sign_on_service_url", utils.TestSingleSignOnURL), ) } @@ -176,7 +179,7 @@ func testAccIdPIntegrationConfig_GSuite_DefaultValues(idpDisplayName string) str } } } - `, idpDisplayName, testSingleSignOnURL) + `, idpDisplayName, utils.TestSingleSignOnURL) } func testAccIdPIntegrationCheck_GSuite_DefaultValues() resource.TestCheckFunc { @@ -184,7 +187,7 @@ func testAccIdPIntegrationCheck_GSuite_DefaultValues() resource.TestCheckFunc { resource.TestMatchResourceAttr("cyral_integration_idp_gsuite.test_idp_integration", "id", regexp.MustCompile(`gsuite.`)), resource.TestCheckResourceAttr("cyral_integration_idp_gsuite.test_idp_integration", - "samlp.0.config.0.single_sign_on_service_url", testSingleSignOnURL), + "samlp.0.config.0.single_sign_on_service_url", utils.TestSingleSignOnURL), ) } @@ -198,7 +201,7 @@ func testAccIdPIntegrationConfig_PingOne_DefaultValues(idpDisplayName string) st } } } - `, idpDisplayName, testSingleSignOnURL) + `, idpDisplayName, utils.TestSingleSignOnURL) } func testAccIdPIntegrationCheck_PingOne_DefaultValues() resource.TestCheckFunc { @@ -206,7 +209,7 @@ func testAccIdPIntegrationCheck_PingOne_DefaultValues() resource.TestCheckFunc { resource.TestMatchResourceAttr("cyral_integration_idp_ping_one.test_idp_integration", "id", regexp.MustCompile(`pingone.`)), resource.TestCheckResourceAttr("cyral_integration_idp_ping_one.test_idp_integration", - "samlp.0.config.0.single_sign_on_service_url", testSingleSignOnURL), + "samlp.0.config.0.single_sign_on_service_url", utils.TestSingleSignOnURL), ) } @@ -220,7 +223,7 @@ func testAccIdPIntegrationConfig_Okta_DefaultValues(idpDisplayName string) strin } } } - `, idpDisplayName, testSingleSignOnURL) + `, idpDisplayName, utils.TestSingleSignOnURL) } func testAccIdPIntegrationCheck_Okta_DefaultValues() resource.TestCheckFunc { @@ -228,7 +231,7 @@ func testAccIdPIntegrationCheck_Okta_DefaultValues() resource.TestCheckFunc { resource.TestMatchResourceAttr("cyral_integration_idp_okta.test_idp_integration", "id", regexp.MustCompile(`okta.`)), resource.TestCheckResourceAttr("cyral_integration_idp_okta.test_idp_integration", - "samlp.0.config.0.single_sign_on_service_url", testSingleSignOnURL), + "samlp.0.config.0.single_sign_on_service_url", utils.TestSingleSignOnURL), ) } @@ -244,7 +247,7 @@ func testAccIdPIntegrationConfig_Updated(idpDisplayName string) string { } } } - `, idpDisplayName, testSingleSignOnURL) + `, idpDisplayName, utils.TestSingleSignOnURL) } func testAccIdPIntegrationCheck_Updated(idpDisplayName string) resource.TestCheckFunc { @@ -256,7 +259,7 @@ func testAccIdPIntegrationCheck_Updated(idpDisplayName string) resource.TestChec resource.TestCheckResourceAttr("cyral_integration_idp_okta.test_idp_integration", "samlp.0.disabled", "true"), resource.TestCheckResourceAttr("cyral_integration_idp_okta.test_idp_integration", - "samlp.0.config.0.single_sign_on_service_url", testSingleSignOnURL), + "samlp.0.config.0.single_sign_on_service_url", utils.TestSingleSignOnURL), resource.TestCheckResourceAttr("cyral_integration_idp_okta.test_idp_integration", "samlp.0.config.0.back_channel_supported", "true"), ) @@ -273,17 +276,17 @@ func testAccIdPIntegrationConfig_NotEmptyAlias(idpDisplayName string) string { } } } - `, accTestName(integrationIdPResourceName, "test-alias"), idpDisplayName, testSingleSignOnURL) + `, utils.AccTestName(utils.IntegrationIdPResourceName, "test-alias"), idpDisplayName, utils.TestSingleSignOnURL) } func testAccIdPIntegrationCheck_NotEmptyAlias() resource.TestCheckFunc { return resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("cyral_integration_idp_okta.test_idp_integration", - "draft_alias", accTestName(integrationIdPResourceName, "test-alias")), + "draft_alias", utils.AccTestName(utils.IntegrationIdPResourceName, "test-alias")), resource.TestCheckResourceAttrPair( "cyral_integration_idp_okta.test_idp_integration", "id", "cyral_integration_idp_okta.test_idp_integration", "draft_alias"), resource.TestCheckResourceAttr("cyral_integration_idp_okta.test_idp_integration", - "samlp.0.config.0.single_sign_on_service_url", testSingleSignOnURL), + "samlp.0.config.0.single_sign_on_service_url", utils.TestSingleSignOnURL), ) } diff --git a/cyral/resource_cyral_integration_logstash.go b/cyral/internal/deprecated/resource_cyral_integration_logstash.go similarity index 79% rename from cyral/resource_cyral_integration_logstash.go rename to cyral/internal/deprecated/resource_cyral_integration_logstash.go index 708e593e..5331891d 100644 --- a/cyral/resource_cyral_integration_logstash.go +++ b/cyral/internal/deprecated/resource_cyral_integration_logstash.go @@ -1,10 +1,11 @@ -package cyral +package deprecated import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -34,43 +35,43 @@ func (data *LogstashIntegration) ReadFromSchema(d *schema.ResourceData) error { return nil } -var ReadLogstashConfig = ResourceOperationConfig{ +var ReadLogstashConfig = core.ResourceOperationConfig{ Name: "LogstashResourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/logstash/%s", c.ControlPlane, d.Id()) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &LogstashIntegration{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &LogstashIntegration{} }, } -func resourceIntegrationLogstash() *schema.Resource { +func ResourceIntegrationLogstash() *schema.Resource { return &schema.Resource{ DeprecationMessage: "Use resource `cyral_integration_logging` instead.", Description: "Manages integration with Logstash.", - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "LogstashResourceCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/logstash", c.ControlPlane) }, - NewResourceData: func() ResourceData { return &LogstashIntegration{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &IDBasedResponse{} }, + NewResourceData: func() core.ResourceData { return &LogstashIntegration{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &core.IDBasedResponse{} }, }, ReadLogstashConfig, ), - ReadContext: ReadResource(ReadLogstashConfig), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadLogstashConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "LogstashResourceUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/logstash/%s", c.ControlPlane, d.Id()) }, - NewResourceData: func() ResourceData { return &LogstashIntegration{} }, + NewResourceData: func() core.ResourceData { return &LogstashIntegration{} }, }, ReadLogstashConfig, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "LogstashResourceDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { diff --git a/cyral/resource_cyral_integration_logstash_test.go b/cyral/internal/deprecated/resource_cyral_integration_logstash_test.go similarity index 69% rename from cyral/resource_cyral_integration_logstash_test.go rename to cyral/internal/deprecated/resource_cyral_integration_logstash_test.go index 8a13c935..041f09bd 100644 --- a/cyral/resource_cyral_integration_logstash_test.go +++ b/cyral/internal/deprecated/resource_cyral_integration_logstash_test.go @@ -1,9 +1,12 @@ -package cyral +package deprecated_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/deprecated" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -11,33 +14,33 @@ const ( integrationLogstashResourceName = "integration-logstash" ) -var initialLogstashConfig LogstashIntegration = LogstashIntegration{ +var initialLogstashConfig deprecated.LogstashIntegration = deprecated.LogstashIntegration{ Endpoint: "logstash.local/", - Name: accTestName(integrationLogstashResourceName, "logstash-test"), + Name: utils.AccTestName(integrationLogstashResourceName, "logstash-test"), UseMutualAuthentication: false, UsePrivateCertificateChain: false, UseTLS: false, } -var updated1LogstashConfig LogstashIntegration = LogstashIntegration{ +var updated1LogstashConfig deprecated.LogstashIntegration = deprecated.LogstashIntegration{ Endpoint: "logstash-updated.local/", - Name: accTestName(integrationLogstashResourceName, "logstash-update-test"), + Name: utils.AccTestName(integrationLogstashResourceName, "logstash-update-test"), UseMutualAuthentication: true, UsePrivateCertificateChain: false, UseTLS: false, } -var updated2LogstashConfig LogstashIntegration = LogstashIntegration{ +var updated2LogstashConfig deprecated.LogstashIntegration = deprecated.LogstashIntegration{ Endpoint: "logstash-updated.local/", - Name: accTestName(integrationLogstashResourceName, "logstash-update-test"), + Name: utils.AccTestName(integrationLogstashResourceName, "logstash-update-test"), UseMutualAuthentication: false, UsePrivateCertificateChain: true, UseTLS: false, } -var updated3LogstashConfig LogstashIntegration = LogstashIntegration{ +var updated3LogstashConfig deprecated.LogstashIntegration = deprecated.LogstashIntegration{ Endpoint: "logstash-updated.local/", - Name: accTestName(integrationLogstashResourceName, "logstash-update-test"), + Name: utils.AccTestName(integrationLogstashResourceName, "logstash-update-test"), UseMutualAuthentication: false, UsePrivateCertificateChain: false, UseTLS: true, @@ -50,7 +53,7 @@ func TestAccLogstashIntegrationResource(t *testing.T) { testUpdate3Config, testUpdate3Func := setupLogstashTest(updated3LogstashConfig) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -77,7 +80,7 @@ func TestAccLogstashIntegrationResource(t *testing.T) { }) } -func setupLogstashTest(integrationData LogstashIntegration) (string, resource.TestCheckFunc) { +func setupLogstashTest(integrationData deprecated.LogstashIntegration) (string, resource.TestCheckFunc) { configuration := formatLogstashIntegrationDataIntoConfig(integrationData) testFunction := resource.ComposeTestCheckFunc( @@ -91,7 +94,7 @@ func setupLogstashTest(integrationData LogstashIntegration) (string, resource.Te return configuration, testFunction } -func formatLogstashIntegrationDataIntoConfig(config LogstashIntegration) string { +func formatLogstashIntegrationDataIntoConfig(config deprecated.LogstashIntegration) string { return fmt.Sprintf(` resource "cyral_integration_logstash" "logstash_integration" { name = "%s" diff --git a/cyral/resource_cyral_integration_looker.go b/cyral/internal/deprecated/resource_cyral_integration_looker.go similarity index 74% rename from cyral/resource_cyral_integration_looker.go rename to cyral/internal/deprecated/resource_cyral_integration_looker.go index d21a215e..db3c9c95 100644 --- a/cyral/resource_cyral_integration_looker.go +++ b/cyral/internal/deprecated/resource_cyral_integration_looker.go @@ -1,10 +1,11 @@ -package cyral +package deprecated import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -28,43 +29,43 @@ func (data *LookerIntegration) ReadFromSchema(d *schema.ResourceData) error { return nil } -var ReadLookerConfig = ResourceOperationConfig{ +var ReadLookerConfig = core.ResourceOperationConfig{ Name: "LookerResourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/looker/%s", c.ControlPlane, d.Id()) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &LookerIntegration{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &LookerIntegration{} }, } -func resourceIntegrationLooker() *schema.Resource { +func ResourceIntegrationLooker() *schema.Resource { return &schema.Resource{ DeprecationMessage: "Integration no longer supported.", Description: "Manages integration with Looker.", - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "LookerResourceCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/looker", c.ControlPlane) }, - NewResourceData: func() ResourceData { return &LookerIntegration{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &IDBasedResponse{} }, + NewResourceData: func() core.ResourceData { return &LookerIntegration{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &core.IDBasedResponse{} }, }, ReadLookerConfig, ), - ReadContext: ReadResource(ReadLookerConfig), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadLookerConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "LookerResourceUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/looker/%s", c.ControlPlane, d.Id()) }, - NewResourceData: func() ResourceData { return &LookerIntegration{} }, + NewResourceData: func() core.ResourceData { return &LookerIntegration{} }, }, ReadLookerConfig, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "LookerResourceDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { diff --git a/cyral/resource_cyral_integration_looker_test.go b/cyral/internal/deprecated/resource_cyral_integration_looker_test.go similarity index 73% rename from cyral/resource_cyral_integration_looker_test.go rename to cyral/internal/deprecated/resource_cyral_integration_looker_test.go index 9a2dc1b1..a73ed9ef 100644 --- a/cyral/resource_cyral_integration_looker_test.go +++ b/cyral/internal/deprecated/resource_cyral_integration_looker_test.go @@ -1,19 +1,21 @@ -package cyral +package deprecated_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/deprecated" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) -var initialLookerConfig LookerIntegration = LookerIntegration{ +var initialLookerConfig deprecated.LookerIntegration = deprecated.LookerIntegration{ ClientId: "lookerClientID", ClientSecret: "lookerClientSecret", URL: "looker.local/", } -var updatedLookerConfig LookerIntegration = LookerIntegration{ +var updatedLookerConfig deprecated.LookerIntegration = deprecated.LookerIntegration{ ClientId: "lookerClientIDUpdated", ClientSecret: "lookerClientSecretUpdated", URL: "looker-updated.local/", @@ -24,7 +26,7 @@ func TestAccLookerIntegrationResource(t *testing.T) { testUpdateConfig, testUpdateFunc := setupLookerTest(updatedLookerConfig) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -43,7 +45,7 @@ func TestAccLookerIntegrationResource(t *testing.T) { }) } -func setupLookerTest(integrationData LookerIntegration) (string, resource.TestCheckFunc) { +func setupLookerTest(integrationData deprecated.LookerIntegration) (string, resource.TestCheckFunc) { configuration := formatLookerIntegrationDataIntoConfig(integrationData) testFunction := resource.ComposeTestCheckFunc( @@ -55,7 +57,7 @@ func setupLookerTest(integrationData LookerIntegration) (string, resource.TestCh return configuration, testFunction } -func formatLookerIntegrationDataIntoConfig(data LookerIntegration) string { +func formatLookerIntegrationDataIntoConfig(data deprecated.LookerIntegration) string { return fmt.Sprintf(` resource "cyral_integration_looker" "looker_integration" { client_id = "%s" diff --git a/cyral/resource_cyral_integration_splunk.go b/cyral/internal/deprecated/resource_cyral_integration_splunk.go similarity index 79% rename from cyral/resource_cyral_integration_splunk.go rename to cyral/internal/deprecated/resource_cyral_integration_splunk.go index 6c72decb..3f71b145 100644 --- a/cyral/resource_cyral_integration_splunk.go +++ b/cyral/internal/deprecated/resource_cyral_integration_splunk.go @@ -1,10 +1,11 @@ -package cyral +package deprecated import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -38,43 +39,43 @@ func (data *SplunkIntegration) ReadFromSchema(d *schema.ResourceData) error { return nil } -var ReadSplunkConfig = ResourceOperationConfig{ +var ReadSplunkConfig = core.ResourceOperationConfig{ Name: "SplunkResourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/splunk/%s", c.ControlPlane, d.Id()) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &SplunkIntegration{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &SplunkIntegration{} }, } -func resourceIntegrationSplunk() *schema.Resource { +func ResourceIntegrationSplunk() *schema.Resource { return &schema.Resource{ DeprecationMessage: "Use resource `cyral_integration_logging` instead.", Description: "Manages [integration with Splunk](https://cyral.com/docs/integrations/siem/splunk/#procedure).", - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "SplunkResourceCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/splunk", c.ControlPlane) }, - NewResourceData: func() ResourceData { return &SplunkIntegration{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &IDBasedResponse{} }, + NewResourceData: func() core.ResourceData { return &SplunkIntegration{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &core.IDBasedResponse{} }, }, ReadSplunkConfig, ), - ReadContext: ReadResource(ReadSplunkConfig), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadSplunkConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "SplunkResourceUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/splunk/%s", c.ControlPlane, d.Id()) }, - NewResourceData: func() ResourceData { return &SplunkIntegration{} }, + NewResourceData: func() core.ResourceData { return &SplunkIntegration{} }, }, ReadSplunkConfig, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "SplunkResourceDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { diff --git a/cyral/resource_cyral_integration_splunk_test.go b/cyral/internal/deprecated/resource_cyral_integration_splunk_test.go similarity index 72% rename from cyral/resource_cyral_integration_splunk_test.go rename to cyral/internal/deprecated/resource_cyral_integration_splunk_test.go index 84b7b250..58152b08 100644 --- a/cyral/resource_cyral_integration_splunk_test.go +++ b/cyral/internal/deprecated/resource_cyral_integration_splunk_test.go @@ -1,9 +1,12 @@ -package cyral +package deprecated_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/deprecated" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -11,8 +14,8 @@ const ( integrationSplunkResourceName = "integration-splunk" ) -var initialSplunkConfig SplunkIntegration = SplunkIntegration{ - Name: accTestName(integrationSplunkResourceName, "splunk-test"), +var initialSplunkConfig deprecated.SplunkIntegration = deprecated.SplunkIntegration{ + Name: utils.AccTestName(integrationSplunkResourceName, "splunk-test"), AccessToken: "access-token", Port: 3333, Host: "splunk.local", @@ -20,8 +23,8 @@ var initialSplunkConfig SplunkIntegration = SplunkIntegration{ UseTLS: false, } -var updatedSplunkConfig SplunkIntegration = SplunkIntegration{ - Name: accTestName(integrationSplunkResourceName, "splunk-test-update"), +var updatedSplunkConfig deprecated.SplunkIntegration = deprecated.SplunkIntegration{ + Name: utils.AccTestName(integrationSplunkResourceName, "splunk-test-update"), AccessToken: "access-token-update", Port: 6666, Host: "splunk-update.local", @@ -34,7 +37,7 @@ func TestAccSplunkIntegrationResource(t *testing.T) { testUpdateConfig, testUpdateFunc := setupSplunkTest(updatedSplunkConfig) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -53,7 +56,7 @@ func TestAccSplunkIntegrationResource(t *testing.T) { }) } -func setupSplunkTest(integrationData SplunkIntegration) (string, resource.TestCheckFunc) { +func setupSplunkTest(integrationData deprecated.SplunkIntegration) (string, resource.TestCheckFunc) { configuration := formatSplunkIntegrationDataIntoConfig(integrationData) testFunction := resource.ComposeTestCheckFunc( @@ -68,7 +71,7 @@ func setupSplunkTest(integrationData SplunkIntegration) (string, resource.TestCh return configuration, testFunction } -func formatSplunkIntegrationDataIntoConfig(data SplunkIntegration) string { +func formatSplunkIntegrationDataIntoConfig(data deprecated.SplunkIntegration) string { return fmt.Sprintf(` resource "cyral_integration_splunk" "splunk_integration" { name = "%s" diff --git a/cyral/resource_cyral_integration_sumo_logic.go b/cyral/internal/deprecated/resource_cyral_integration_sumo_logic.go similarity index 72% rename from cyral/resource_cyral_integration_sumo_logic.go rename to cyral/internal/deprecated/resource_cyral_integration_sumo_logic.go index 9dcb0b5f..e177ea9b 100644 --- a/cyral/resource_cyral_integration_sumo_logic.go +++ b/cyral/internal/deprecated/resource_cyral_integration_sumo_logic.go @@ -1,10 +1,11 @@ -package cyral +package deprecated import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -25,43 +26,43 @@ func (data *SumoLogicIntegration) ReadFromSchema(d *schema.ResourceData) error { return nil } -var ReadSumoLogicConfig = ResourceOperationConfig{ +var ReadSumoLogicConfig = core.ResourceOperationConfig{ Name: "SumoLogicResourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/sumologic/%s", c.ControlPlane, d.Id()) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &SumoLogicIntegration{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &SumoLogicIntegration{} }, } -func resourceIntegrationSumoLogic() *schema.Resource { +func ResourceIntegrationSumoLogic() *schema.Resource { return &schema.Resource{ DeprecationMessage: "Use resource `cyral_integration_logging` instead.", Description: "Manages integration with [Sumo Logic to push sidecar logs](https://cyral.com/docs/integrations/siem/sumo-logic/).", - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "SumoLogicResourceCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/sumologic", c.ControlPlane) }, - NewResourceData: func() ResourceData { return &SumoLogicIntegration{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &IDBasedResponse{} }, + NewResourceData: func() core.ResourceData { return &SumoLogicIntegration{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &core.IDBasedResponse{} }, }, ReadSumoLogicConfig, ), - ReadContext: ReadResource(ReadSumoLogicConfig), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadSumoLogicConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "SumoLogicResourceUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/sumologic/%s", c.ControlPlane, d.Id()) }, - NewResourceData: func() ResourceData { return &SumoLogicIntegration{} }, + NewResourceData: func() core.ResourceData { return &SumoLogicIntegration{} }, }, ReadSumoLogicConfig, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "SumoLogicResourceDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { diff --git a/cyral/resource_cyral_integration_sumo_logic_test.go b/cyral/internal/deprecated/resource_cyral_integration_sumo_logic_test.go similarity index 62% rename from cyral/resource_cyral_integration_sumo_logic_test.go rename to cyral/internal/deprecated/resource_cyral_integration_sumo_logic_test.go index 181559da..90e8fb0d 100644 --- a/cyral/resource_cyral_integration_sumo_logic_test.go +++ b/cyral/internal/deprecated/resource_cyral_integration_sumo_logic_test.go @@ -1,9 +1,12 @@ -package cyral +package deprecated_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/deprecated" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -11,13 +14,13 @@ const ( integrationSumoLogicResourceName = "integration-sumo-logic" ) -var initialSumoLogicConfig SumoLogicIntegration = SumoLogicIntegration{ - Name: accTestName(integrationSumoLogicResourceName, "sumo-logic"), +var initialSumoLogicConfig deprecated.SumoLogicIntegration = deprecated.SumoLogicIntegration{ + Name: utils.AccTestName(integrationSumoLogicResourceName, "sumo-logic"), Address: "https://sumologic.local/initial", } -var updatedSumoLogicConfig SumoLogicIntegration = SumoLogicIntegration{ - Name: accTestName(integrationSumoLogicResourceName, "sumo-logic-updated"), +var updatedSumoLogicConfig deprecated.SumoLogicIntegration = deprecated.SumoLogicIntegration{ + Name: utils.AccTestName(integrationSumoLogicResourceName, "sumo-logic-updated"), Address: "https://sumologic.local/updated", } @@ -26,7 +29,7 @@ func TestAccSumoLogicIntegrationResource(t *testing.T) { testUpdateConfig, testUpdateFunc := setupSumoLogicTest(updatedSumoLogicConfig) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -45,7 +48,7 @@ func TestAccSumoLogicIntegrationResource(t *testing.T) { }) } -func setupSumoLogicTest(integrationData SumoLogicIntegration) (string, resource.TestCheckFunc) { +func setupSumoLogicTest(integrationData deprecated.SumoLogicIntegration) (string, resource.TestCheckFunc) { configuration := formatSumoLogicIntegrationDataIntoConfig(integrationData) testFunction := resource.ComposeTestCheckFunc( @@ -56,7 +59,7 @@ func setupSumoLogicTest(integrationData SumoLogicIntegration) (string, resource. return configuration, testFunction } -func formatSumoLogicIntegrationDataIntoConfig(data SumoLogicIntegration) string { +func formatSumoLogicIntegrationDataIntoConfig(data deprecated.SumoLogicIntegration) string { return fmt.Sprintf(` resource "cyral_integration_sumo_logic" "sumo_logic_integration" { name = "%s" diff --git a/cyral/resource_cyral_integration_aws_iam.go b/cyral/internal/integration/awsiam/resource_cyral_integration_aws_iam.go similarity index 80% rename from cyral/resource_cyral_integration_aws_iam.go rename to cyral/internal/integration/awsiam/resource_cyral_integration_aws_iam.go index bb5d58e3..33e9980f 100644 --- a/cyral/resource_cyral_integration_aws_iam.go +++ b/cyral/internal/integration/awsiam/resource_cyral_integration_aws_iam.go @@ -1,10 +1,12 @@ -package cyral +package awsiam import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -60,7 +62,7 @@ func (wrapper *AWSIAMIntegrationWrapper) ReadFromSchema(d *schema.ResourceData) return nil } -var ReadAWSIAMIntegration = ResourceOperationConfig{ +var ReadAWSIAMIntegration = core.ResourceOperationConfig{ Name: "AWSIAMIntegrationRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -70,34 +72,34 @@ var ReadAWSIAMIntegration = ResourceOperationConfig{ d.Id(), ) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &AWSIAMIntegrationWrapper{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "AWS IAM Integration"}, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "AWS IAM Integration"}, } -func resourceIntegrationAWSIAM() *schema.Resource { +func ResourceIntegrationAWSIAM() *schema.Resource { return &schema.Resource{ Description: "Authenticate users based on AWS IAM credentials.", - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "AWSIAMIntegrationCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/aws/iam", c.ControlPlane) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return &AWSIAMIntegrationWrapper{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { - return &IDBasedResponse{} + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { + return &core.IDBasedResponse{} }, }, ReadAWSIAMIntegration, ), - ReadContext: ReadResource(ReadAWSIAMIntegration), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadAWSIAMIntegration), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "AWSIAMIntegrationUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -107,14 +109,14 @@ func resourceIntegrationAWSIAM() *schema.Resource { d.Id(), ) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return &AWSIAMIntegrationWrapper{} }, }, ReadAWSIAMIntegration, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "AWSIAMIntegrationDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -132,7 +134,7 @@ func resourceIntegrationAWSIAM() *schema.Resource { }, Schema: map[string]*schema.Schema{ - IDKey: { + utils.IDKey: { Description: "ID of this resource in Cyral environment.", Type: schema.TypeString, Computed: true, diff --git a/cyral/resource_cyral_integration_aws_iam_test.go b/cyral/internal/integration/awsiam/resource_cyral_integration_aws_iam_test.go similarity index 95% rename from cyral/resource_cyral_integration_aws_iam_test.go rename to cyral/internal/integration/awsiam/resource_cyral_integration_aws_iam_test.go index d51cf51f..c6f16d94 100644 --- a/cyral/resource_cyral_integration_aws_iam_test.go +++ b/cyral/internal/integration/awsiam/resource_cyral_integration_aws_iam_test.go @@ -1,9 +1,11 @@ -package cyral +package awsiam_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -13,7 +15,7 @@ const ( ) func TestIntegrationAWSIAMAuthN(t *testing.T) { - resourceName := accTestName("aws-iam-integration", "main_test") + resourceName := utils.AccTestName("aws-iam-integration", "main_test") testCreate := getCreateStep(resourceName) testUpdate1 := getUpdateStepRemoveARN(resourceName) testUpdate2 := getUpdateStepAddARN(resourceName) @@ -21,7 +23,7 @@ func TestIntegrationAWSIAMAuthN(t *testing.T) { testUpdate4 := getUpdateStepChangeDescription(resourceName) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ testCreate, testUpdate1, diff --git a/cyral/resource_cyral_integration_mfa_duo.go b/cyral/internal/integration/confextension/mfaduo/resource_cyral_integration_mfa_duo.go similarity index 66% rename from cyral/resource_cyral_integration_mfa_duo.go rename to cyral/internal/integration/confextension/mfaduo/resource_cyral_integration_mfa_duo.go index 137ffd2d..9d0fe918 100644 --- a/cyral/resource_cyral_integration_mfa_duo.go +++ b/cyral/internal/integration/confextension/mfaduo/resource_cyral_integration_mfa_duo.go @@ -1,21 +1,23 @@ -package cyral +package mfaduo import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + ce "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/confextension" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) -func resourceIntegrationMFADuo() *schema.Resource { +func ResourceIntegrationMFADuo() *schema.Resource { return &schema.Resource{ Description: "Manages [integration with Duo MFA](https://cyral.com/docs/mfa/duo).", - CreateContext: CreateResource( - ConfExtensionIntegrationCreate(duoMFATemplateType), - ConfExtensionIntegrationRead(duoMFATemplateType)), - ReadContext: ReadResource(ConfExtensionIntegrationRead(duoMFATemplateType)), - UpdateContext: UpdateResource( - ConfExtensionIntegrationUpdate(duoMFATemplateType), - ConfExtensionIntegrationRead(duoMFATemplateType)), - DeleteContext: DeleteResource(ConfExtensionIntegrationDelete(duoMFATemplateType)), + CreateContext: core.CreateResource( + ce.ConfExtensionIntegrationCreate(ce.DuoMFATemplateType), + ce.ConfExtensionIntegrationRead(ce.DuoMFATemplateType)), + ReadContext: core.ReadResource(ce.ConfExtensionIntegrationRead(ce.DuoMFATemplateType)), + UpdateContext: core.UpdateResource( + ce.ConfExtensionIntegrationUpdate(ce.DuoMFATemplateType), + ce.ConfExtensionIntegrationRead(ce.DuoMFATemplateType)), + DeleteContext: core.DeleteResource(ce.ConfExtensionIntegrationDelete(ce.DuoMFATemplateType)), Schema: map[string]*schema.Schema{ "id": { diff --git a/cyral/resource_cyral_integration_mfa_duo_test.go b/cyral/internal/integration/confextension/mfaduo/resource_cyral_integration_mfa_duo_test.go similarity index 72% rename from cyral/resource_cyral_integration_mfa_duo_test.go rename to cyral/internal/integration/confextension/mfaduo/resource_cyral_integration_mfa_duo_test.go index 8074c40b..950ada6a 100644 --- a/cyral/resource_cyral_integration_mfa_duo_test.go +++ b/cyral/internal/integration/confextension/mfaduo/resource_cyral_integration_mfa_duo_test.go @@ -1,10 +1,13 @@ -package cyral +package mfaduo_test import ( "encoding/json" "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/confextension" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -12,16 +15,16 @@ const ( integrationDuoMFAResourceName = "integration-duo-mfa" ) -func initialDuoMFAIntegrationConfig() *IntegrationConfExtension { - integration := NewIntegrationConfExtension(duoMFATemplateType) - integration.Name = accTestName(integrationDuoMFAResourceName, "integration") +func initialDuoMFAIntegrationConfig() *confextension.IntegrationConfExtension { + integration := confextension.NewIntegrationConfExtension(confextension.DuoMFATemplateType) + integration.Name = utils.AccTestName(integrationDuoMFAResourceName, "integration") integration.Parameters = `{"integrationKey": "integration-key-1", "secretKey": "secret-key-1", "apiHostname": "api-hostname-1"}` return integration } -func updatedDuoMFAIntegrationConfig() *IntegrationConfExtension { - integration := NewIntegrationConfExtension(duoMFATemplateType) - integration.Name = accTestName(integrationDuoMFAResourceName, "integration-updated") +func updatedDuoMFAIntegrationConfig() *confextension.IntegrationConfExtension { + integration := confextension.NewIntegrationConfExtension(confextension.DuoMFATemplateType) + integration.Name = utils.AccTestName(integrationDuoMFAResourceName, "integration-updated") integration.Parameters = `{"integrationKey": "integration-key-2", "secretKey": "secret-key-2", "apiHostname": "api-hostname-2"}` return integration } @@ -35,7 +38,7 @@ func TestAccDuoMFAIntegrationResource(t *testing.T) { updatedDuoMFAIntegrationConfig(), tfResourceName) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -56,11 +59,11 @@ func TestAccDuoMFAIntegrationResource(t *testing.T) { func setupDuoMFAIntegrationTest( t *testing.T, - integrationData *IntegrationConfExtension, + integrationData *confextension.IntegrationConfExtension, resourceName string, ) (string, resource.TestCheckFunc) { - var parameters IntegrationConfExtensionParameters + var parameters confextension.IntegrationConfExtensionParameters err := json.Unmarshal([]byte(integrationData.Parameters), ¶meters) if err != nil { t.Fatalf("Error unmarshalling parameters: %v", err) diff --git a/cyral/model_integration_confextension.go b/cyral/internal/integration/confextension/model_integration_confextension.go similarity index 76% rename from cyral/model_integration_confextension.go rename to cyral/internal/integration/confextension/model_integration_confextension.go index 779d94e1..9c0be0e9 100644 --- a/cyral/model_integration_confextension.go +++ b/cyral/internal/integration/confextension/model_integration_confextension.go @@ -1,11 +1,12 @@ -package cyral +package confextension import ( "encoding/json" "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -14,8 +15,8 @@ const ( authorizationPurpose = "authorization" builtinCategory = "builtin" - pagerDutyTemplateType = "pagerduty" - duoMFATemplateType = "duoMfa" + PagerDutyTemplateType = "pagerduty" + DuoMFATemplateType = "duoMfa" ) type IntegrationConfExtension struct { @@ -53,9 +54,9 @@ func (data *IntegrationConfExtension) WriteToSchema(d *schema.ResourceData) erro } switch data.TemplateType { - case pagerDutyTemplateType: + case PagerDutyTemplateType: d.Set("api_token", parameters.APIToken) - case duoMFATemplateType: + case DuoMFATemplateType: d.Set("integration_key", parameters.IntegrationKey) d.Set("secret_key", parameters.SecretKey) d.Set("api_hostname", parameters.APIHostname) @@ -70,9 +71,9 @@ func (data *IntegrationConfExtension) ReadFromSchema(d *schema.ResourceData) err var parameters IntegrationConfExtensionParameters switch data.TemplateType { - case pagerDutyTemplateType: + case PagerDutyTemplateType: parameters.APIToken = d.Get("api_token").(string) - case duoMFATemplateType: + case DuoMFATemplateType: parameters.IntegrationKey = d.Get("integration_key").(string) parameters.SecretKey = d.Get("secret_key").(string) parameters.APIHostname = d.Get("api_hostname").(string) @@ -87,8 +88,8 @@ func (data *IntegrationConfExtension) ReadFromSchema(d *schema.ResourceData) err return nil } -func ConfExtensionIntegrationCreate(templateType string) ResourceOperationConfig { - return ResourceOperationConfig{ +func ConfExtensionIntegrationCreate(templateType string) core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: fmt.Sprintf("%s_IntegrationResourceCreate", templateType), HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -96,15 +97,15 @@ func ConfExtensionIntegrationCreate(templateType string) ResourceOperationConfig "https://%s/v1/integrations/confExtensions/instances", c.ControlPlane, ) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return NewIntegrationConfExtension(templateType) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &IDBasedResponse{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &core.IDBasedResponse{} }, } } -func ConfExtensionIntegrationRead(templateType string) ResourceOperationConfig { - return ResourceOperationConfig{ +func ConfExtensionIntegrationRead(templateType string) core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: fmt.Sprintf("%s_IntegrationResourceRead", templateType), HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -113,14 +114,14 @@ func ConfExtensionIntegrationRead(templateType string) ResourceOperationConfig { c.ControlPlane, d.Id(), ) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return NewIntegrationConfExtension(templateType) }, } } -func ConfExtensionIntegrationUpdate(templateType string) ResourceOperationConfig { - return ResourceOperationConfig{ +func ConfExtensionIntegrationUpdate(templateType string) core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: fmt.Sprintf("%s_IntegrationResourceUpdate", templateType), HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -128,14 +129,14 @@ func ConfExtensionIntegrationUpdate(templateType string) ResourceOperationConfig "https://%s/v1/integrations/confExtensions/instances/%s", c.ControlPlane, d.Id(), ) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return NewIntegrationConfExtension(templateType) }, } } -func ConfExtensionIntegrationDelete(templateType string) ResourceOperationConfig { - return ResourceOperationConfig{ +func ConfExtensionIntegrationDelete(templateType string) core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: fmt.Sprintf("%s_IntegrationResourceDelete", templateType), HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { diff --git a/cyral/resource_cyral_integration_pager_duty.go b/cyral/internal/integration/confextension/pagerduty/resource_cyral_integration_pager_duty.go similarity index 53% rename from cyral/resource_cyral_integration_pager_duty.go rename to cyral/internal/integration/confextension/pagerduty/resource_cyral_integration_pager_duty.go index 82a4c139..ce4f48ef 100644 --- a/cyral/resource_cyral_integration_pager_duty.go +++ b/cyral/internal/integration/confextension/pagerduty/resource_cyral_integration_pager_duty.go @@ -1,20 +1,22 @@ -package cyral +package pagerduty import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + ce "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/confextension" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -func resourceIntegrationPagerDuty() *schema.Resource { +func ResourceIntegrationPagerDuty() *schema.Resource { return &schema.Resource{ Description: "Manages [integration with PagerDuty](https://cyral.com/docs/integrations/incident-response/pagerduty/#in-cyral).", - CreateContext: CreateResource( - ConfExtensionIntegrationCreate(pagerDutyTemplateType), - ConfExtensionIntegrationRead(pagerDutyTemplateType)), - ReadContext: ReadResource(ConfExtensionIntegrationRead(pagerDutyTemplateType)), - UpdateContext: UpdateResource( - ConfExtensionIntegrationUpdate(pagerDutyTemplateType), - ConfExtensionIntegrationRead(pagerDutyTemplateType)), - DeleteContext: DeleteResource(ConfExtensionIntegrationDelete(pagerDutyTemplateType)), + CreateContext: core.CreateResource( + ce.ConfExtensionIntegrationCreate(ce.PagerDutyTemplateType), + ce.ConfExtensionIntegrationRead(ce.PagerDutyTemplateType)), + ReadContext: core.ReadResource(ce.ConfExtensionIntegrationRead(ce.PagerDutyTemplateType)), + UpdateContext: core.UpdateResource( + ce.ConfExtensionIntegrationUpdate(ce.PagerDutyTemplateType), + ce.ConfExtensionIntegrationRead(ce.PagerDutyTemplateType)), + DeleteContext: core.DeleteResource(ce.ConfExtensionIntegrationDelete(ce.PagerDutyTemplateType)), Schema: map[string]*schema.Schema{ "id": { diff --git a/cyral/resource_cyral_integration_pager_duty_test.go b/cyral/internal/integration/confextension/pagerduty/resource_cyral_integration_pager_duty_test.go similarity index 64% rename from cyral/resource_cyral_integration_pager_duty_test.go rename to cyral/internal/integration/confextension/pagerduty/resource_cyral_integration_pager_duty_test.go index a98d69d5..6e2aa089 100644 --- a/cyral/resource_cyral_integration_pager_duty_test.go +++ b/cyral/internal/integration/confextension/pagerduty/resource_cyral_integration_pager_duty_test.go @@ -1,9 +1,12 @@ -package cyral +package pagerduty_test import ( "fmt" "testing" + ce "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/confextension" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -11,16 +14,16 @@ const ( integrationPagerDutyResourceName = "integration-pager-duty" ) -func initialPagerDutyIntegrationConfig() *IntegrationConfExtension { - integration := NewIntegrationConfExtension(pagerDutyTemplateType) - integration.Name = accTestName(integrationPagerDutyResourceName, "pager-duty") +func initialPagerDutyIntegrationConfig() *ce.IntegrationConfExtension { + integration := ce.NewIntegrationConfExtension(ce.PagerDutyTemplateType) + integration.Name = utils.AccTestName(integrationPagerDutyResourceName, "pager-duty") integration.Parameters = "unitTest-parameters" return integration } -func updatedPagerDutyIntegrationConfig() *IntegrationConfExtension { - integration := NewIntegrationConfExtension(pagerDutyTemplateType) - integration.Name = accTestName(integrationPagerDutyResourceName, "pager-duty-updated") +func updatedPagerDutyIntegrationConfig() *ce.IntegrationConfExtension { + integration := ce.NewIntegrationConfExtension(ce.PagerDutyTemplateType) + integration.Name = utils.AccTestName(integrationPagerDutyResourceName, "pager-duty-updated") integration.Parameters = "unitTest-parameters-updated" return integration } @@ -30,7 +33,7 @@ func TestAccPagerDutyIntegrationResource(t *testing.T) { testUpdateConfig, testUpdateFunc := setupPagerDutyIntegrationTest(updatedPagerDutyIntegrationConfig()) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -49,7 +52,7 @@ func TestAccPagerDutyIntegrationResource(t *testing.T) { }) } -func setupPagerDutyIntegrationTest(integrationData *IntegrationConfExtension) (string, resource.TestCheckFunc) { +func setupPagerDutyIntegrationTest(integrationData *ce.IntegrationConfExtension) (string, resource.TestCheckFunc) { configuration := formatPagerDutyIntegrationIntoConfig( integrationData.Name, integrationData.Parameters) diff --git a/cyral/model_cyral_integration_hcvault.go b/cyral/internal/integration/hcvault/model_cyral_integration_hcvault.go similarity index 96% rename from cyral/model_cyral_integration_hcvault.go rename to cyral/internal/integration/hcvault/model_cyral_integration_hcvault.go index cd3d520a..bf31a06b 100644 --- a/cyral/model_cyral_integration_hcvault.go +++ b/cyral/internal/integration/hcvault/model_cyral_integration_hcvault.go @@ -1,4 +1,4 @@ -package cyral +package hcvault // HCVaultIntegration defines the necessary data for Hashicorp Vault integration type HCVaultIntegration struct { diff --git a/cyral/resource_cyral_integration_hcvault.go b/cyral/internal/integration/hcvault/resource_cyral_integration_hcvault.go similarity index 73% rename from cyral/resource_cyral_integration_hcvault.go rename to cyral/internal/integration/hcvault/resource_cyral_integration_hcvault.go index 7d772999..980c7b5d 100644 --- a/cyral/resource_cyral_integration_hcvault.go +++ b/cyral/internal/integration/hcvault/resource_cyral_integration_hcvault.go @@ -1,10 +1,11 @@ -package cyral +package hcvault import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -26,43 +27,43 @@ func (data *HCVaultIntegration) ReadFromSchema(d *schema.ResourceData) error { return nil } -var ReadHCVaultIntegrationConfig = ResourceOperationConfig{ +var ReadHCVaultIntegrationConfig = core.ResourceOperationConfig{ Name: "HCVaultIntegrationResourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/secretProviders/hcvault/%s", c.ControlPlane, d.Id()) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &HCVaultIntegration{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "Integration hcvault"}, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &HCVaultIntegration{} }, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Integration hcvault"}, } -func resourceIntegrationHCVault() *schema.Resource { +func ResourceIntegrationHCVault() *schema.Resource { return &schema.Resource{ Description: "Manages integration with Hashicorp Vault to store secrets.", - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "HCVaultIntegrationResourceCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/secretProviders/hcvault", c.ControlPlane) }, - NewResourceData: func() ResourceData { return &HCVaultIntegration{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &IDBasedResponse{} }, + NewResourceData: func() core.ResourceData { return &HCVaultIntegration{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &core.IDBasedResponse{} }, }, ReadHCVaultIntegrationConfig, ), - ReadContext: ReadResource(ReadHCVaultIntegrationConfig), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadHCVaultIntegrationConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "HCVaultIntegrationResourceUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/secretProviders/hcvault/%s", c.ControlPlane, d.Id()) }, - NewResourceData: func() ResourceData { return &HCVaultIntegration{} }, + NewResourceData: func() core.ResourceData { return &HCVaultIntegration{} }, }, ReadHCVaultIntegrationConfig, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "HCVaultIntegrationResourceDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { diff --git a/cyral/resource_cyral_integration_hcvault_test.go b/cyral/internal/integration/hcvault/resource_cyral_integration_hcvault_test.go similarity index 66% rename from cyral/resource_cyral_integration_hcvault_test.go rename to cyral/internal/integration/hcvault/resource_cyral_integration_hcvault_test.go index 3879aab1..77a68874 100644 --- a/cyral/resource_cyral_integration_hcvault_test.go +++ b/cyral/internal/integration/hcvault/resource_cyral_integration_hcvault_test.go @@ -1,29 +1,33 @@ -package cyral +package hcvault_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/hcvault" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const ( integrationHCVaultResourceName = "integration-hcvault" ) -var initialHCVaultIntegrationConfig HCVaultIntegration = HCVaultIntegration{ +var initialHCVaultIntegrationConfig hcvault.HCVaultIntegration = hcvault.HCVaultIntegration{ AuthMethod: "unitTest-auth_method", ID: "unitTest-id", AuthType: "unitTest-auth_type", - Name: accTestName(integrationHCVaultResourceName, "hcvault"), + Name: utils.AccTestName(integrationHCVaultResourceName, "hcvault"), Server: "unitTest-server", } -var updatedHCVaultIntegrationConfig HCVaultIntegration = HCVaultIntegration{ +var updatedHCVaultIntegrationConfig hcvault.HCVaultIntegration = hcvault.HCVaultIntegration{ AuthMethod: "unitTest-auth_method-updated", ID: "unitTest-id-updated", AuthType: "unitTest-auth_type-updated", - Name: accTestName(integrationHCVaultResourceName, "hcvault-updated"), + Name: utils.AccTestName(integrationHCVaultResourceName, "hcvault-updated"), Server: "unitTest-server-updated", } @@ -32,7 +36,11 @@ func TestAccHCVaultIntegrationResource(t *testing.T) { testUpdateConfig, testUpdateFunc := setupHCVaultIntegrationTest(updatedHCVaultIntegrationConfig) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: testConfig, @@ -51,7 +59,7 @@ func TestAccHCVaultIntegrationResource(t *testing.T) { }) } -func setupHCVaultIntegrationTest(integrationData HCVaultIntegration) (string, resource.TestCheckFunc) { +func setupHCVaultIntegrationTest(integrationData hcvault.HCVaultIntegration) (string, resource.TestCheckFunc) { configuration := formatHCVaultIntegrationDataIntoConfig(integrationData) testFunction := resource.ComposeTestCheckFunc( @@ -65,7 +73,7 @@ func setupHCVaultIntegrationTest(integrationData HCVaultIntegration) (string, re return configuration, testFunction } -func formatHCVaultIntegrationDataIntoConfig(data HCVaultIntegration) string { +func formatHCVaultIntegrationDataIntoConfig(data hcvault.HCVaultIntegration) string { return fmt.Sprintf(` resource "cyral_integration_hc_vault" "hc_vault_integration" { auth_method = "%s" diff --git a/cyral/data_source_cyral_integration_idp_saml.go b/cyral/internal/integration/idpsaml/data_source_cyral_integration_idp_saml.go similarity index 92% rename from cyral/data_source_cyral_integration_idp_saml.go rename to cyral/internal/integration/idpsaml/data_source_cyral_integration_idp_saml.go index 600ef3f5..b5e55227 100644 --- a/cyral/data_source_cyral_integration_idp_saml.go +++ b/cyral/internal/integration/idpsaml/data_source_cyral_integration_idp_saml.go @@ -1,4 +1,4 @@ -package cyral +package idpsaml import ( "fmt" @@ -7,7 +7,9 @@ import ( "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" ) type ListGenericSAMLIdpsRequest struct { @@ -63,25 +65,25 @@ func (resp *ListGenericSAMLIdpsResponse) WriteToSchema(d *schema.ResourceData) e return nil } -func dataSourceIntegrationIdPSAMLReadConfig() ResourceOperationConfig { - return ResourceOperationConfig{ +func dataSourceIntegrationIdPSAMLReadConfig() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "IntegrationIdPSAMLDataSourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { - query := urlQuery(map[string]string{ + query := utils.UrlQuery(map[string]string{ "displayName": d.Get("display_name").(string), "idpType": d.Get("idp_type").(string), }) return fmt.Sprintf("https://%s/v1/integrations/generic-saml/sso%s", c.ControlPlane, query) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &ListGenericSAMLIdpsResponse{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &ListGenericSAMLIdpsResponse{} }, } } -func dataSourceIntegrationIdPSAML() *schema.Resource { +func DataSourceIntegrationIdPSAML() *schema.Resource { return &schema.Resource{ Description: "Retrieve and filter SAML IdP integrations.", - ReadContext: ReadResource(dataSourceIntegrationIdPSAMLReadConfig()), + ReadContext: core.ReadResource(dataSourceIntegrationIdPSAMLReadConfig()), Schema: map[string]*schema.Schema{ "display_name": { Description: "Filter results by the display name (as seen in the control plane UI) of existing SAML IdP integrations.", diff --git a/cyral/data_source_cyral_integration_idp_saml_test.go b/cyral/internal/integration/idpsaml/data_source_cyral_integration_idp_saml_test.go similarity index 78% rename from cyral/data_source_cyral_integration_idp_saml_test.go rename to cyral/internal/integration/idpsaml/data_source_cyral_integration_idp_saml_test.go index 97d909ae..b77b352b 100644 --- a/cyral/data_source_cyral_integration_idp_saml_test.go +++ b/cyral/internal/integration/idpsaml/data_source_cyral_integration_idp_saml_test.go @@ -1,11 +1,15 @@ -package cyral +package idpsaml_test import ( "fmt" "regexp" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/idpsaml" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/require" ) @@ -13,24 +17,24 @@ const ( integrationIdPSAMLDataSourceName = "data-integration-idp-saml" ) -func integrationIdPSAMLDataSourceTestIdps() []GenericSAMLIntegration { - return []GenericSAMLIntegration{ +func integrationIdPSAMLDataSourceTestIdps() []idpsaml.GenericSAMLIntegration { + return []idpsaml.GenericSAMLIntegration{ { ID: "id-1", - DisplayName: accTestName( + DisplayName: utils.AccTestName( integrationIdPSAMLDataSourceName, "integration-1"), IdpType: "idp-type-1", Disabled: false, - IdpDescriptor: &GenericSAMLIdpDescriptor{ + IdpDescriptor: &idpsaml.GenericSAMLIdpDescriptor{ SingleSignOnServiceURL: "sso-url-1", SigningCertificate: "signing-certificate-1", DisableForceAuthentication: false, SingleLogoutServiceURL: "slo-url-1", }, - SPMetadata: &GenericSAMLSPMetadata{ + SPMetadata: &idpsaml.GenericSAMLSPMetadata{ XMLDocument: "xml-document-1", }, - Attributes: NewRequiredUserAttributes( + Attributes: idpsaml.NewRequiredUserAttributes( "first-name-1", "last-name-1", "email-1", @@ -39,20 +43,20 @@ func integrationIdPSAMLDataSourceTestIdps() []GenericSAMLIntegration { }, { ID: "id-2", - DisplayName: accTestName( + DisplayName: utils.AccTestName( integrationIdPSAMLDataSourceName, "integration-2"), IdpType: "idp-type-2", Disabled: true, - IdpDescriptor: &GenericSAMLIdpDescriptor{ + IdpDescriptor: &idpsaml.GenericSAMLIdpDescriptor{ SingleSignOnServiceURL: "sso-url-2", SigningCertificate: "signing-certificate-2", DisableForceAuthentication: true, SingleLogoutServiceURL: "slo-url-2", }, - SPMetadata: &GenericSAMLSPMetadata{ + SPMetadata: &idpsaml.GenericSAMLSPMetadata{ XMLDocument: "xml-document-2", }, - Attributes: NewRequiredUserAttributes( + Attributes: idpsaml.NewRequiredUserAttributes( "first-name-2", "last-name-2", "email-2", @@ -63,11 +67,11 @@ func integrationIdPSAMLDataSourceTestIdps() []GenericSAMLIntegration { } func testIntegrationIdPSAMLDataSourceName1() string { - return accTestName(integrationIdPSAMLDataSourceName, "1") + return utils.AccTestName(integrationIdPSAMLDataSourceName, "1") } func testIntegrationIdPSAMLDataSourceName2() string { - return accTestName(integrationIdPSAMLDataSourceName, "2") + return utils.AccTestName(integrationIdPSAMLDataSourceName, "2") } func TestAccIntegrationIdPSAMLDataSource(t *testing.T) { @@ -77,7 +81,11 @@ func TestAccIntegrationIdPSAMLDataSource(t *testing.T) { "test2", testIntegrationIdPSAMLDataSourceName2(), "type2") resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: testConfig1, @@ -104,7 +112,7 @@ func testIntegrationIdPSAMLDataSourceConfigDependencies(resName string) string { resName2 := resName + "_2" var config string - config += formatBasicIntegrationIdPSAMLDraftIntoConfig( + config += utils.FormatBasicIntegrationIdPSAMLDraftIntoConfig( resName1, testIntegrationIdPSAMLDataSourceName1(), "type1") @@ -112,7 +120,7 @@ func testIntegrationIdPSAMLDataSourceConfigDependencies(resName string) string { resName1, resName1, samlMetadataDocumentSample("fake-certificate")) - config += formatBasicIntegrationIdPSAMLDraftIntoConfig( + config += utils.FormatBasicIntegrationIdPSAMLDraftIntoConfig( resName2, testIntegrationIdPSAMLDataSourceName2(), "type2") @@ -198,16 +206,16 @@ func testIntegrationIdPSAMLDataSourceChecks(t *testing.T, resName, nameFilter, t "idp_list.0.attributes.#", "1", ), resource.TestCheckResourceAttr(dataSourceFullName, - "idp_list.0.attributes.0.first_name", defaultUserAttributeFirstName, + "idp_list.0.attributes.0.first_name", idpsaml.DefaultUserAttributeFirstName, ), resource.TestCheckResourceAttr(dataSourceFullName, - "idp_list.0.attributes.0.last_name", defaultUserAttributeLastName, + "idp_list.0.attributes.0.last_name", idpsaml.DefaultUserAttributeLastName, ), resource.TestCheckResourceAttr(dataSourceFullName, - "idp_list.0.attributes.0.email", defaultUserAttributeEmail, + "idp_list.0.attributes.0.email", idpsaml.DefaultUserAttributeEmail, ), resource.TestCheckResourceAttr(dataSourceFullName, - "idp_list.0.attributes.0.groups", defaultUserAttributeGroups, + "idp_list.0.attributes.0.groups", idpsaml.DefaultUserAttributeGroups, ), } @@ -222,8 +230,8 @@ func testIntegrationIdPSAMLDataSourceChecks(t *testing.T, resName, nameFilter, t return resource.ComposeTestCheckFunc(testFunctions...) } -func filterSAMLIdps(idps []GenericSAMLIntegration, nameFilter, typeFilter string) []GenericSAMLIntegration { - var filteredIdps []GenericSAMLIntegration +func filterSAMLIdps(idps []idpsaml.GenericSAMLIntegration, nameFilter, typeFilter string) []idpsaml.GenericSAMLIntegration { + var filteredIdps []idpsaml.GenericSAMLIntegration for _, idp := range idps { if (nameFilter == "" || idp.DisplayName == nameFilter) && (typeFilter == "" || idp.IdpType == typeFilter) { @@ -241,5 +249,5 @@ func integrationIdPSAMLDataSourceConfig(resName string, dependsOn []string, depends_on = %s display_name = "%s" idp_type = "%s" - }`, resName, listToStr(dependsOn), nameFilter, typeFilter) + }`, resName, utils.ListToStr(dependsOn), nameFilter, typeFilter) } diff --git a/cyral/model_integration_idp_generic_saml.go b/cyral/internal/integration/idpsaml/model_integration_idp_generic_saml.go similarity index 91% rename from cyral/model_integration_idp_generic_saml.go rename to cyral/internal/integration/idpsaml/model_integration_idp_generic_saml.go index 3a95619c..0695135b 100644 --- a/cyral/model_integration_idp_generic_saml.go +++ b/cyral/internal/integration/idpsaml/model_integration_idp_generic_saml.go @@ -1,4 +1,4 @@ -package cyral +package idpsaml import ( "fmt" @@ -7,10 +7,10 @@ import ( ) const ( - defaultUserAttributeFirstName = "firstName" - defaultUserAttributeLastName = "lastName" - defaultUserAttributeEmail = "email" - defaultUserAttributeGroups = "memberOf" + DefaultUserAttributeFirstName = "firstName" + DefaultUserAttributeLastName = "lastName" + DefaultUserAttributeEmail = "email" + DefaultUserAttributeGroups = "memberOf" ) type GenericSAMLDraft struct { @@ -163,3 +163,17 @@ type GenericSAMLIdpMetadata struct { URL string `json:"url,omitempty"` XML string `json:"xml,omitempty"` } + +type KeycloakProvider struct{} + +type IdentityProviderData struct { + Keycloak KeycloakProvider `json:"keycloakProvider"` +} + +func (data IdentityProviderData) WriteToSchema(d *schema.ResourceData) error { + return nil +} + +func (data *IdentityProviderData) ReadFromSchema(d *schema.ResourceData) error { + return nil +} diff --git a/cyral/resource_cyral_integration_idp_saml.go b/cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml.go similarity index 75% rename from cyral/resource_cyral_integration_idp_saml.go rename to cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml.go index e9cc6cdd..eafe58c4 100644 --- a/cyral/resource_cyral_integration_idp_saml.go +++ b/cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml.go @@ -1,4 +1,4 @@ -package cyral +package idpsaml import ( "fmt" @@ -7,7 +7,9 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" ) func GenericSAMLIdPInfoArguments() []string { @@ -55,44 +57,44 @@ func (resp *ReadGenericSAMLResponse) WriteToSchema(d *schema.ResourceData) error return resp.IdentityProvider.WriteToSchema(d) } -func CreateGenericSAMLConfig() ResourceOperationConfig { - return ResourceOperationConfig{ +func CreateGenericSAMLConfig() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "GenericSAMLResourceCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/generic-saml/sso", c.ControlPlane) }, - NewResourceData: func() ResourceData { return &CreateGenericSAMLRequest{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &CreateGenericSAMLResponse{} }, + NewResourceData: func() core.ResourceData { return &CreateGenericSAMLRequest{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &CreateGenericSAMLResponse{} }, } } -func CreateIdPConfig() ResourceOperationConfig { - return ResourceOperationConfig{ +func CreateIdPConfig() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "GenericSAMLResourceValidation", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/conf/identityProviders/%s", c.ControlPlane, d.Id()) }, - NewResourceData: func() ResourceData { return &IdentityProviderData{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &IdentityProviderData{} }, + NewResourceData: func() core.ResourceData { return &IdentityProviderData{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &IdentityProviderData{} }, } } -func ReadGenericSAMLConfig() ResourceOperationConfig { - return ResourceOperationConfig{ +func ReadGenericSAMLConfig() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "GenericSAMLResourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/generic-saml/sso/%s", c.ControlPlane, d.Id()) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &ReadGenericSAMLResponse{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "Generic SAML"}, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &ReadGenericSAMLResponse{} }, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Generic SAML"}, } } -func DeleteGenericSAMLConfig() ResourceOperationConfig { - return ResourceOperationConfig{ +func DeleteGenericSAMLConfig() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "GenericSAMLResourceDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -101,29 +103,29 @@ func DeleteGenericSAMLConfig() ResourceOperationConfig { } } -func resourceIntegrationIdPSAML() *schema.Resource { +func ResourceIntegrationIdPSAML() *schema.Resource { return &schema.Resource{ Description: "Manages identity provider (IdP) integrations using SAML to allow " + "[Single Sing-On](https://cyral.com/docs/sso/overview) to Cyral.\n\nSee also " + "the remaining SAML-related resources and data sources.", - CreateContext: CRUDResources( - []ResourceOperation{ + CreateContext: core.CRUDResources( + []core.ResourceOperation{ { - Type: create, + Type: core.OperationTypeCreate, Config: CreateGenericSAMLConfig(), }, { - Type: read, + Type: core.OperationTypeRead, Config: ReadGenericSAMLConfig(), }, { - Type: create, + Type: core.OperationTypeCreate, Config: CreateIdPConfig(), }, }, ), - ReadContext: ReadResource(ReadGenericSAMLConfig()), - DeleteContext: DeleteResource(DeleteGenericSAMLConfig()), + ReadContext: core.ReadResource(ReadGenericSAMLConfig()), + DeleteContext: core.DeleteResource(DeleteGenericSAMLConfig()), Schema: map[string]*schema.Schema{ // Input arguments // @@ -132,7 +134,7 @@ func resourceIntegrationIdPSAML() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validationStringLenAtLeast(5), + ValidateFunc: utils.ValidationStringLenAtLeast(5), }, // Do not allow in-place updates of idp_metadata_url or // idp_metadata_xml. This would require us to parse the diff --git a/cyral/resource_cyral_integration_idp_saml_draft.go b/cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml_draft.go similarity index 85% rename from cyral/resource_cyral_integration_idp_saml_draft.go rename to cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml_draft.go index c1301db9..afcb19ff 100644 --- a/cyral/resource_cyral_integration_idp_saml_draft.go +++ b/cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml_draft.go @@ -1,4 +1,4 @@ -package cyral +package idpsaml import ( "encoding/json" @@ -9,7 +9,9 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" ) // Note that although the IdP type is SAML as seen from the control plane UI, we @@ -72,7 +74,7 @@ func (resp *GenericSAMLDraftResponse) WriteToSchema(d *schema.ResourceData) erro if err := d.Set("idp_type", resp.Draft.IdpType); err != nil { return err } - if resp.Draft.Attributes != nil && typeSetNonEmpty(d, "attributes") { + if resp.Draft.Attributes != nil && utils.TypeSetNonEmpty(d, "attributes") { if err := resp.Draft.Attributes.WriteToSchema(d); err != nil { return err } @@ -84,51 +86,51 @@ type ListGenericSAMLDraftsResponse struct { Drafts []GenericSAMLDraft `json:"drafts"` } -func CreateGenericSAMLDraftConfig() ResourceOperationConfig { - return ResourceOperationConfig{ +func CreateGenericSAMLDraftConfig() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "GenericSAMLDraftResourceCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/generic-saml/drafts", c.ControlPlane) }, - NewResourceData: func() ResourceData { return &CreateGenericSAMLDraftRequest{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &GenericSAMLDraftResponse{} }, + NewResourceData: func() core.ResourceData { return &CreateGenericSAMLDraftRequest{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &GenericSAMLDraftResponse{} }, } } -func ReadGenericSAMLDraftConfig() ResourceOperationConfig { - return ResourceOperationConfig{ +func ReadGenericSAMLDraftConfig() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "GenericSAMLDraftResourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/generic-saml/drafts/%s", c.ControlPlane, d.Id()) }, RequestErrorHandler: &readGenericSAMLDraftErrorHandler{}, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &GenericSAMLDraftResponse{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &GenericSAMLDraftResponse{} }, } } -func DeleteGenericSAMLDraftConfig() ResourceOperationConfig { - return ResourceOperationConfig{ +func DeleteGenericSAMLDraftConfig() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "GenericSAMLDraftResourceDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/generic-saml/drafts/%s", c.ControlPlane, d.Id()) }, - RequestErrorHandler: &DeleteIgnoreHttpNotFound{resName: "SAML draft"}, + RequestErrorHandler: &core.DeleteIgnoreHttpNotFound{ResName: "SAML draft"}, } } -func resourceIntegrationIdPSAMLDraft() *schema.Resource { +func ResourceIntegrationIdPSAMLDraft() *schema.Resource { return &schema.Resource{ Description: "Manages SAML IdP integration drafts." + "\n\nSee also the remaining SAML-related resources and data sources.", - CreateContext: CreateResource( + CreateContext: core.CreateResource( CreateGenericSAMLDraftConfig(), ReadGenericSAMLDraftConfig(), ), - ReadContext: ReadResource(ReadGenericSAMLDraftConfig()), - DeleteContext: DeleteResource(DeleteGenericSAMLDraftConfig()), + ReadContext: core.ReadResource(ReadGenericSAMLDraftConfig()), + DeleteContext: core.DeleteResource(DeleteGenericSAMLDraftConfig()), Schema: map[string]*schema.Schema{ // All of the input arguments must force recreation of // the resource, because the API does not support @@ -169,32 +171,32 @@ func resourceIntegrationIdPSAMLDraft() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - Default: defaultUserAttributeFirstName, - ValidateFunc: validationStringLenAtLeast(3), + Default: DefaultUserAttributeFirstName, + ValidateFunc: utils.ValidationStringLenAtLeast(3), }, "last_name": { Description: "The name of the attribute in the incoming SAML assertion containing the users last name (family name). Defaults to `lastName`.", Type: schema.TypeString, Optional: true, ForceNew: true, - Default: defaultUserAttributeLastName, - ValidateFunc: validationStringLenAtLeast(3), + Default: DefaultUserAttributeLastName, + ValidateFunc: utils.ValidationStringLenAtLeast(3), }, "email": { Description: "The name of the attribute in the incoming SAML assertion containing the users email address. Defaults to `email`.", Type: schema.TypeString, Optional: true, ForceNew: true, - Default: defaultUserAttributeEmail, - ValidateFunc: validationStringLenAtLeast(3), + Default: DefaultUserAttributeEmail, + ValidateFunc: utils.ValidationStringLenAtLeast(3), }, "groups": { Description: "The name of the attribute in the incoming SAML assertion containing the users group membership in the IdP. Defaults to `memberOf`.", Type: schema.TypeString, Optional: true, ForceNew: true, - Default: defaultUserAttributeGroups, - ValidateFunc: validationStringLenAtLeast(3), + Default: DefaultUserAttributeGroups, + ValidateFunc: utils.ValidationStringLenAtLeast(3), }, }, }, @@ -280,7 +282,7 @@ func (h *readGenericSAMLDraftErrorHandler) HandleError( } log.Printf("[DEBUG] SAML draft not found. Checking if completed draft exists.") - query := urlQuery(map[string]string{ + query := utils.UrlQuery(map[string]string{ "includeCompletedDrafts": "true", "displayName": d.Get("display_name").(string), "idpType": d.Get("idp_type").(string), diff --git a/cyral/resource_cyral_integration_idp_saml_draft_test.go b/cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml_draft_test.go similarity index 80% rename from cyral/resource_cyral_integration_idp_saml_draft_test.go rename to cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml_draft_test.go index bdb18115..002a0d45 100644 --- a/cyral/resource_cyral_integration_idp_saml_draft_test.go +++ b/cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml_draft_test.go @@ -1,4 +1,4 @@ -package cyral +package idpsaml_test import ( "fmt" @@ -6,7 +6,11 @@ import ( "strconv" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/idpsaml" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/require" ) @@ -14,13 +18,13 @@ const ( integrationIdPSAMLDraftResourceName = "integration-idp-saml-draft" ) -func genericSAMLDraftConfigInitial() *GenericSAMLDraft { - return &GenericSAMLDraft{ - DisplayName: accTestName( +func genericSAMLDraftConfigInitial() *idpsaml.GenericSAMLDraft { + return &idpsaml.GenericSAMLDraft{ + DisplayName: utils.AccTestName( integrationIdPSAMLDraftResourceName, "integration-1"), DisableIdPInitiatedLogin: false, IdpType: "some-idp-type-1", - Attributes: NewRequiredUserAttributes( + Attributes: idpsaml.NewRequiredUserAttributes( "first-name-1", "last-name-1", "email-1", @@ -29,13 +33,13 @@ func genericSAMLDraftConfigInitial() *GenericSAMLDraft { } } -func genericSAMLDraftConfigUpdated() *GenericSAMLDraft { - return &GenericSAMLDraft{ - DisplayName: accTestName( +func genericSAMLDraftConfigUpdated() *idpsaml.GenericSAMLDraft { + return &idpsaml.GenericSAMLDraft{ + DisplayName: utils.AccTestName( integrationIdPSAMLDraftResourceName, "integration-2"), DisableIdPInitiatedLogin: true, IdpType: "some-idp-type-2", - Attributes: NewRequiredUserAttributes( + Attributes: idpsaml.NewRequiredUserAttributes( "first-name-2", "last-name-2", "email-2", @@ -44,9 +48,9 @@ func genericSAMLDraftConfigUpdated() *GenericSAMLDraft { } } -func genericSAMLDraftConfigNoAttributes() *GenericSAMLDraft { - return &GenericSAMLDraft{ - DisplayName: accTestName( +func genericSAMLDraftConfigNoAttributes() *idpsaml.GenericSAMLDraft { + return &idpsaml.GenericSAMLDraft{ + DisplayName: utils.AccTestName( integrationIdPSAMLDraftResourceName, "integration-2"), DisableIdPInitiatedLogin: true, IdpType: "some-idp-type-2", @@ -62,7 +66,11 @@ func TestAccIntegrationIdPSAMLDraftResource(t *testing.T) { genericSAMLDraftConfigNoAttributes(), "no_attributes") resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: initialConfig, @@ -86,7 +94,7 @@ func TestAccIntegrationIdPSAMLDraftResource(t *testing.T) { }) } -func setupIntegrationIdPSAMLDraftTest(t *testing.T, draft *GenericSAMLDraft, resName string) ( +func setupIntegrationIdPSAMLDraftTest(t *testing.T, draft *idpsaml.GenericSAMLDraft, resName string) ( string, resource.TestCheckFunc, ) { @@ -139,7 +147,7 @@ func setupIntegrationIdPSAMLDraftTest(t *testing.T, draft *GenericSAMLDraft, res return config, resource.ComposeTestCheckFunc(checkFuncs...) } -func formatGenericSAMLDraftIntoConfig(draft *GenericSAMLDraft, resName string) string { +func formatGenericSAMLDraftIntoConfig(draft *idpsaml.GenericSAMLDraft, resName string) string { var attributesStr string if draft.Attributes != nil { attributesStr += fmt.Sprintf(` diff --git a/cyral/resource_cyral_integration_idp_saml_test.go b/cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml_test.go similarity index 88% rename from cyral/resource_cyral_integration_idp_saml_test.go rename to cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml_test.go index 140b567a..c47bfc54 100644 --- a/cyral/resource_cyral_integration_idp_saml_test.go +++ b/cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml_test.go @@ -1,4 +1,4 @@ -package cyral +package idpsaml_test import ( "encoding/base64" @@ -6,7 +6,10 @@ import ( "regexp" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const ( @@ -48,7 +51,11 @@ func TestAccIntegrationIdPSAMLResource(t *testing.T) { "new_test", samlMetadataDocumentSample("fakeCertificateNew")) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: initialConfig, @@ -87,8 +94,8 @@ func setupIntegrationIdPSAMLTest(resName, metadataDoc string) ( resource.TestCheckFunc, ) { var config string - config += formatBasicIntegrationIdPSAMLDraftIntoConfig(resName, - accTestName(integrationIdPSAMLResourceName, "saml-draft"), + config += utils.FormatBasicIntegrationIdPSAMLDraftIntoConfig(resName, + utils.AccTestName(integrationIdPSAMLResourceName, "saml-draft"), "some-idp-type") config += integrationIdPSAMLResourceConfig(resName, resName, metadataDoc) diff --git a/cyral/data_source_cyral_integration_logging.go b/cyral/internal/integration/logging/data_source_cyral_integration_logging.go similarity index 76% rename from cyral/data_source_cyral_integration_logging.go rename to cyral/internal/integration/logging/data_source_cyral_integration_logging.go index af22d39a..e6246329 100644 --- a/cyral/data_source_cyral_integration_logging.go +++ b/cyral/internal/integration/logging/data_source_cyral_integration_logging.go @@ -1,10 +1,12 @@ -package cyral +package logging import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -38,28 +40,28 @@ func (resp *ListIntegrationLogsResponse) WriteToSchema(d *schema.ResourceData) e return nil } -func dataSourceIntegrationLogsRead() ResourceOperationConfig { - return ResourceOperationConfig{ +func dataSourceIntegrationLogsRead() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "IntegrationLogsDataSourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { - query := urlQuery(map[string]string{ + query := utils.UrlQuery(map[string]string{ "type": d.Get("type").(string), }) return fmt.Sprintf("https://%s/v1/integrations/logging%s", c.ControlPlane, query) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &ListIntegrationLogsResponse{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &ListIntegrationLogsResponse{} }, } } -func dataSourceIntegrationLogging() *schema.Resource { +func DataSourceIntegrationLogging() *schema.Resource { rawSchema := getIntegrationLogsSchema() // all fields in integrations are computed. // this function changes the schema to achieve this - computedSchema := convertSchemaFieldsToComputed(rawSchema) + computedSchema := utils.ConvertSchemaFieldsToComputed(rawSchema) return &schema.Resource{ Description: "Retrieve and filter logging integrations.", - ReadContext: ReadResource(dataSourceIntegrationLogsRead()), + ReadContext: core.ReadResource(dataSourceIntegrationLogsRead()), Schema: map[string]*schema.Schema{ "type": { Description: "The type of logging integration config to filter by.", diff --git a/cyral/data_source_cyral_integration_logging_test.go b/cyral/internal/integration/logging/data_source_cyral_integration_logging_test.go similarity index 75% rename from cyral/data_source_cyral_integration_logging_test.go rename to cyral/internal/integration/logging/data_source_cyral_integration_logging_test.go index 72d5ef21..1d9eb947 100644 --- a/cyral/data_source_cyral_integration_logging_test.go +++ b/cyral/internal/integration/logging/data_source_cyral_integration_logging_test.go @@ -1,10 +1,14 @@ -package cyral +package logging_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/logging" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const ( @@ -36,11 +40,11 @@ func testIntegrationLoggingDataSourceConfigDependencies(resName string) string { resName1 := resName + "_1" resName2 := resName + "_2" - resource1, _ := formatLogsIntegrationDataIntoConfig(LoggingIntegration{ + resource1, _ := formatLogsIntegrationDataIntoConfig(logging.LoggingIntegration{ Name: resName1, ReceiveAuditLogs: true, - LoggingIntegrationConfig: LoggingIntegrationConfig{ - CloudWatch: &CloudWatchConfig{ + LoggingIntegrationConfig: logging.LoggingIntegrationConfig{ + CloudWatch: &logging.CloudWatchConfig{ Region: "us-east-2", Group: "group2", Stream: "abcd", @@ -48,11 +52,11 @@ func testIntegrationLoggingDataSourceConfigDependencies(resName string) string { }, }, resName1) - resource2, _ := formatLogsIntegrationDataIntoConfig(LoggingIntegration{ + resource2, _ := formatLogsIntegrationDataIntoConfig(logging.LoggingIntegration{ Name: resName2, ReceiveAuditLogs: true, - LoggingIntegrationConfig: LoggingIntegrationConfig{ - Datadog: &DataDogConfig{ + LoggingIntegrationConfig: logging.LoggingIntegrationConfig{ + Datadog: &logging.DataDogConfig{ ApiKey: "API_KEY_A", }, }, @@ -66,12 +70,16 @@ func testIntegrationLoggingDataSourceConfigDependencies(resName string) string { func TestAccLoggingIntegrationDataSource(t *testing.T) { testConfig1, testFunc1 := testIntegrationLoggingDataSource(t, - accTestName(integrationLogsDataSourceName, "test1"), "CLOUDWATCH") + utils.AccTestName(integrationLogsDataSourceName, "test1"), "CLOUDWATCH") testConfig2, testFunc2 := testIntegrationLoggingDataSource(t, - accTestName(integrationLogsDataSourceName, "test2"), "DATADOG") + utils.AccTestName(integrationLogsDataSourceName, "test2"), "DATADOG") resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: testConfig1, @@ -125,5 +133,5 @@ func integrationLogsDataSourceConfig(resName string, typeFilter string, dependsO data "cyral_integration_logging" "%s" { depends_on = %s type = "%s" - }`, resName, listToStr(dependsOn), typeFilter) + }`, resName, utils.ListToStr(dependsOn), typeFilter) } diff --git a/cyral/model_integration_logging.go b/cyral/internal/integration/logging/model_integration_logging.go similarity index 99% rename from cyral/model_integration_logging.go rename to cyral/internal/integration/logging/model_integration_logging.go index a839ba08..4ac8f340 100644 --- a/cyral/model_integration_logging.go +++ b/cyral/internal/integration/logging/model_integration_logging.go @@ -1,4 +1,4 @@ -package cyral +package logging import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" diff --git a/cyral/resource_cyral_integration_logging.go b/cyral/internal/integration/logging/resource_cyral_integration_logging.go similarity index 83% rename from cyral/resource_cyral_integration_logging.go rename to cyral/internal/integration/logging/resource_cyral_integration_logging.go index 704f804c..93532c30 100644 --- a/cyral/resource_cyral_integration_logging.go +++ b/cyral/internal/integration/logging/resource_cyral_integration_logging.go @@ -1,4 +1,4 @@ -package cyral +package logging import ( "fmt" @@ -6,7 +6,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" ) const loggingApiUrl = "https://%s/v1/integrations/logging/%s" @@ -170,41 +171,41 @@ func (integrationLogConfig *LoggingIntegration) ReadFromSchema(d *schema.Resourc return nil } -func CreateLoggingIntegration() ResourceOperationConfig { - return ResourceOperationConfig{ +func CreateLoggingIntegration() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "LoggingIntegrationCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/logging", c.ControlPlane) }, - NewResourceData: func() ResourceData { return &LoggingIntegration{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &IDBasedResponse{} }, + NewResourceData: func() core.ResourceData { return &LoggingIntegration{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &core.IDBasedResponse{} }, } } -var ReadLoggingIntegration = ResourceOperationConfig{ +var ReadLoggingIntegration = core.ResourceOperationConfig{ Name: "LoggingIntegrationRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf(loggingApiUrl, c.ControlPlane, d.Id()) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &LoggingIntegration{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "Integration logging"}, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &LoggingIntegration{} }, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Integration logging"}, } -func UpdateLoggingIntegration() ResourceOperationConfig { - return ResourceOperationConfig{ +func UpdateLoggingIntegration() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "LoggingIntegrationUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf(loggingApiUrl, c.ControlPlane, d.Id()) }, - NewResourceData: func() ResourceData { return &LoggingIntegration{} }, + NewResourceData: func() core.ResourceData { return &LoggingIntegration{} }, } } -func DeleteLoggingIntegration() ResourceOperationConfig { - return ResourceOperationConfig{ +func DeleteLoggingIntegration() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "LoggingIntegrationDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -213,17 +214,17 @@ func DeleteLoggingIntegration() ResourceOperationConfig { } } -func resourceIntegrationLogging() *schema.Resource { +func ResourceIntegrationLogging() *schema.Resource { return &schema.Resource{ Description: "Manages a logging integration that can be used to push logs from Cyral to the corresponding logging system (E.g.: AWS CloudWatch, Splunk, SumoLogic, etc).", - CreateContext: CreateResource( + CreateContext: core.CreateResource( CreateLoggingIntegration(), ReadLoggingIntegration, ), - ReadContext: ReadResource(ReadLoggingIntegration), - UpdateContext: UpdateResource(UpdateLoggingIntegration(), ReadLoggingIntegration), - DeleteContext: DeleteResource(DeleteLoggingIntegration()), + ReadContext: core.ReadResource(ReadLoggingIntegration), + UpdateContext: core.UpdateResource(UpdateLoggingIntegration(), ReadLoggingIntegration), + DeleteContext: core.DeleteResource(DeleteLoggingIntegration()), Schema: getIntegrationLogsSchema(), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, diff --git a/cyral/resource_cyral_integration_logging_test.go b/cyral/internal/integration/logging/resource_cyral_integration_logging_test.go similarity index 70% rename from cyral/resource_cyral_integration_logging_test.go rename to cyral/internal/integration/logging/resource_cyral_integration_logging_test.go index 6b566082..a5ee5486 100644 --- a/cyral/resource_cyral_integration_logging_test.go +++ b/cyral/internal/integration/logging/resource_cyral_integration_logging_test.go @@ -1,4 +1,4 @@ -package cyral +package logging_test import ( "fmt" @@ -6,7 +6,11 @@ import ( "strings" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/logging" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const ( @@ -14,11 +18,17 @@ const ( integrationLogsFullTerraformResourceName = "cyral_integration_logging.logs_integration_test" ) -var initialLogsConfigCloudWatch LoggingIntegration = LoggingIntegration{ - Name: accTestName(integrationLogsResourceName, "LogsCloudWatchTest"), +var ProviderFactories = map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, +} + +var initialLogsConfigCloudWatch logging.LoggingIntegration = logging.LoggingIntegration{ + Name: utils.AccTestName(integrationLogsResourceName, "LogsCloudWatchTest"), ReceiveAuditLogs: true, - LoggingIntegrationConfig: LoggingIntegrationConfig{ - CloudWatch: &CloudWatchConfig{ + LoggingIntegrationConfig: logging.LoggingIntegrationConfig{ + CloudWatch: &logging.CloudWatchConfig{ Region: "us-east-2", Group: "group2", Stream: "abcd", @@ -26,23 +36,23 @@ var initialLogsConfigCloudWatch LoggingIntegration = LoggingIntegration{ }, } -var initialLogsConfigDataDog LoggingIntegration = LoggingIntegration{ - Name: accTestName(integrationLogsResourceName, "Datadog"), +var initialLogsConfigDataDog logging.LoggingIntegration = logging.LoggingIntegration{ + Name: utils.AccTestName(integrationLogsResourceName, "Datadog"), ReceiveAuditLogs: true, - LoggingIntegrationConfig: LoggingIntegrationConfig{ - Datadog: &DataDogConfig{ + LoggingIntegrationConfig: logging.LoggingIntegrationConfig{ + Datadog: &logging.DataDogConfig{ ApiKey: "TESTING_API", }, }, } -var initialLogsConfigElk LoggingIntegration = LoggingIntegration{ - Name: accTestName(integrationLogsResourceName, "LogsElkComplete"), +var initialLogsConfigElk logging.LoggingIntegration = logging.LoggingIntegration{ + Name: utils.AccTestName(integrationLogsResourceName, "LogsElkComplete"), ReceiveAuditLogs: true, - LoggingIntegrationConfig: LoggingIntegrationConfig{ - Elk: &ElkConfig{ + LoggingIntegrationConfig: logging.LoggingIntegrationConfig{ + Elk: &logging.ElkConfig{ EsURL: "http://es.com", KibanaURL: "http://kibana.com", - EsCredentials: &EsCredentials{ + EsCredentials: &logging.EsCredentials{ Username: "gabriel", Password: "123", }, @@ -50,21 +60,21 @@ var initialLogsConfigElk LoggingIntegration = LoggingIntegration{ }, } -var initialLogsConfigElkEmptyEsCredentials LoggingIntegration = LoggingIntegration{ - Name: accTestName(integrationLogsResourceName, "LogsElkEmptyEsCredentials"), +var initialLogsConfigElkEmptyEsCredentials logging.LoggingIntegration = logging.LoggingIntegration{ + Name: utils.AccTestName(integrationLogsResourceName, "LogsElkEmptyEsCredentials"), ReceiveAuditLogs: true, - LoggingIntegrationConfig: LoggingIntegrationConfig{ - Elk: &ElkConfig{ + LoggingIntegrationConfig: logging.LoggingIntegrationConfig{ + Elk: &logging.ElkConfig{ EsURL: "http://es.com", }, }, } -var initialLogsConfigSplunk LoggingIntegration = LoggingIntegration{ - Name: accTestName(integrationLogsResourceName, "Splunk"), +var initialLogsConfigSplunk logging.LoggingIntegration = logging.LoggingIntegration{ + Name: utils.AccTestName(integrationLogsResourceName, "Splunk"), ReceiveAuditLogs: true, - LoggingIntegrationConfig: LoggingIntegrationConfig{ - Splunk: &SplunkConfig{ + LoggingIntegrationConfig: logging.LoggingIntegrationConfig{ + Splunk: &logging.SplunkConfig{ Hostname: "www.splunk.com", HecPort: "9529", AccessToken: "ACCESS", @@ -74,21 +84,21 @@ var initialLogsConfigSplunk LoggingIntegration = LoggingIntegration{ }, } -var initialLogsConfigSumologic LoggingIntegration = LoggingIntegration{ - Name: accTestName(integrationLogsResourceName, "Sumologic"), +var initialLogsConfigSumologic logging.LoggingIntegration = logging.LoggingIntegration{ + Name: utils.AccTestName(integrationLogsResourceName, "Sumologic"), ReceiveAuditLogs: true, - LoggingIntegrationConfig: LoggingIntegrationConfig{ - SumoLogic: &SumoLogicConfig{ + LoggingIntegrationConfig: logging.LoggingIntegrationConfig{ + SumoLogic: &logging.SumoLogicConfig{ Address: "https://www.hostname.com.br/path", }, }, } -var initialLogsConfigFluentbit LoggingIntegration = LoggingIntegration{ - Name: accTestName(integrationLogsResourceName, "Fluentbit"), +var initialLogsConfigFluentbit logging.LoggingIntegration = logging.LoggingIntegration{ + Name: utils.AccTestName(integrationLogsResourceName, "Fluentbit"), ReceiveAuditLogs: true, - LoggingIntegrationConfig: LoggingIntegrationConfig{ - FluentBit: &FluentBitConfig{ + LoggingIntegrationConfig: logging.LoggingIntegrationConfig{ + FluentBit: &logging.FluentBitConfig{ Config: `[OUTPUT] Name stdout Match *`, @@ -96,11 +106,11 @@ Match *`, }, } -var updatedLogsConfigCloudWatch LoggingIntegration = LoggingIntegration{ - Name: accTestName(integrationLogsResourceName, "LogsCloudWatchTest"), +var updatedLogsConfigCloudWatch logging.LoggingIntegration = logging.LoggingIntegration{ + Name: utils.AccTestName(integrationLogsResourceName, "LogsCloudWatchTest"), ReceiveAuditLogs: true, - LoggingIntegrationConfig: LoggingIntegrationConfig{ - CloudWatch: &CloudWatchConfig{ + LoggingIntegrationConfig: logging.LoggingIntegrationConfig{ + CloudWatch: &logging.CloudWatchConfig{ Region: "us-east-1", Group: "group1", Stream: "abcd", @@ -108,24 +118,24 @@ var updatedLogsConfigCloudWatch LoggingIntegration = LoggingIntegration{ }, } -var updatedLogsConfigDataDog LoggingIntegration = LoggingIntegration{ - Name: accTestName(integrationLogsResourceName, "Datadog"), +var updatedLogsConfigDataDog logging.LoggingIntegration = logging.LoggingIntegration{ + Name: utils.AccTestName(integrationLogsResourceName, "Datadog"), ReceiveAuditLogs: true, - LoggingIntegrationConfig: LoggingIntegrationConfig{ - Datadog: &DataDogConfig{ + LoggingIntegrationConfig: logging.LoggingIntegrationConfig{ + Datadog: &logging.DataDogConfig{ ApiKey: "TESTING_API", }, }, } -var updatedLogsConfigElk LoggingIntegration = LoggingIntegration{ - Name: accTestName(integrationLogsResourceName, "LogsElkComplete"), +var updatedLogsConfigElk logging.LoggingIntegration = logging.LoggingIntegration{ + Name: utils.AccTestName(integrationLogsResourceName, "LogsElkComplete"), ReceiveAuditLogs: true, - LoggingIntegrationConfig: LoggingIntegrationConfig{ - Elk: &ElkConfig{ + LoggingIntegrationConfig: logging.LoggingIntegrationConfig{ + Elk: &logging.ElkConfig{ EsURL: "http://esupdate.com", KibanaURL: "http://kibanaupdate.com", - EsCredentials: &EsCredentials{ + EsCredentials: &logging.EsCredentials{ Username: "gabriel-update", Password: "1234", }, @@ -133,21 +143,21 @@ var updatedLogsConfigElk LoggingIntegration = LoggingIntegration{ }, } -var updatedLogsConfigElkEmptyEsCredentials LoggingIntegration = LoggingIntegration{ - Name: accTestName(integrationLogsResourceName, "LogsElkEmptyEsCredentials"), +var updatedLogsConfigElkEmptyEsCredentials logging.LoggingIntegration = logging.LoggingIntegration{ + Name: utils.AccTestName(integrationLogsResourceName, "LogsElkEmptyEsCredentials"), ReceiveAuditLogs: true, - LoggingIntegrationConfig: LoggingIntegrationConfig{ - Elk: &ElkConfig{ + LoggingIntegrationConfig: logging.LoggingIntegrationConfig{ + Elk: &logging.ElkConfig{ EsURL: "http://esupdate1.com", }, }, } -var updatedLogsConfigSplunk LoggingIntegration = LoggingIntegration{ - Name: accTestName(integrationLogsResourceName, "Splunk"), +var updatedLogsConfigSplunk logging.LoggingIntegration = logging.LoggingIntegration{ + Name: utils.AccTestName(integrationLogsResourceName, "Splunk"), ReceiveAuditLogs: true, - LoggingIntegrationConfig: LoggingIntegrationConfig{ - Splunk: &SplunkConfig{ + LoggingIntegrationConfig: logging.LoggingIntegrationConfig{ + Splunk: &logging.SplunkConfig{ Hostname: "www.splunk2.com", HecPort: "8090", AccessToken: "ACCESS", @@ -157,21 +167,21 @@ var updatedLogsConfigSplunk LoggingIntegration = LoggingIntegration{ }, } -var updatedLogsConfigSumologic LoggingIntegration = LoggingIntegration{ - Name: accTestName(integrationLogsResourceName, "Sumologic"), +var updatedLogsConfigSumologic logging.LoggingIntegration = logging.LoggingIntegration{ + Name: utils.AccTestName(integrationLogsResourceName, "Sumologic"), ReceiveAuditLogs: true, - LoggingIntegrationConfig: LoggingIntegrationConfig{ - SumoLogic: &SumoLogicConfig{ + LoggingIntegrationConfig: logging.LoggingIntegrationConfig{ + SumoLogic: &logging.SumoLogicConfig{ Address: "https://www.hostnameupdated.com.br/path", }, }, } -var updatedLogsConfigFluentbit LoggingIntegration = LoggingIntegration{ - Name: accTestName(integrationLogsResourceName, "Fluentbit"), +var updatedLogsConfigFluentbit logging.LoggingIntegration = logging.LoggingIntegration{ + Name: utils.AccTestName(integrationLogsResourceName, "Fluentbit"), ReceiveAuditLogs: false, - LoggingIntegrationConfig: LoggingIntegrationConfig{ - FluentBit: &FluentBitConfig{ + LoggingIntegrationConfig: logging.LoggingIntegrationConfig{ + FluentBit: &logging.FluentBitConfig{ Config: `[OUTPUT] Name stdout Match *`, @@ -184,7 +194,7 @@ func TestAccLogsIntegrationResourceCloudWatch(t *testing.T) { testUpdateConfig, testUpdateFunc := setupLogsTest(updatedLogsConfigCloudWatch) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -208,7 +218,7 @@ func TestAccLogsIntegrationResourceDataDog(t *testing.T) { testUpdateConfig, testUpdateFunc := setupLogsTest(updatedLogsConfigDataDog) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -232,7 +242,7 @@ func TestAccLogsIntegrationResourceElk(t *testing.T) { testUpdateConfig, testUpdateFunc := setupLogsTest(updatedLogsConfigElk) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -256,7 +266,7 @@ func TestAccLogsIntegrationResourceElkEmptyEsCredentials(t *testing.T) { testUpdateConfig, testUpdateFunc := setupLogsTest(updatedLogsConfigElkEmptyEsCredentials) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -280,7 +290,7 @@ func TestAccLogsIntegrationResourceSplunk(t *testing.T) { testUpdateConfig, testUpdateFunc := setupLogsTest(updatedLogsConfigSplunk) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -304,7 +314,7 @@ func TestAccLogsIntegrationResourceSumologic(t *testing.T) { testUpdateConfig, testUpdateFunc := setupLogsTest(updatedLogsConfigSumologic) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -328,7 +338,7 @@ func TestAccLogsIntegrationResourceFluentbit(t *testing.T) { testUpdateConfig, testUpdateFunc := setupLogsTest(updatedLogsConfigFluentbit) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -347,7 +357,7 @@ func TestAccLogsIntegrationResourceFluentbit(t *testing.T) { }) } -func setupLogsTest(integrationData LoggingIntegration) (string, resource.TestCheckFunc) { +func setupLogsTest(integrationData logging.LoggingIntegration) (string, resource.TestCheckFunc) { configuration, err := formatLogsIntegrationDataIntoConfig(integrationData, "logs_integration_test") if err != nil { log.Fatalf("%v", err) @@ -432,7 +442,7 @@ func setupLogsTest(integrationData LoggingIntegration) (string, resource.TestChe // this function formats LoggingIntegration into string. // this is also used in datasource tests -func formatLogsIntegrationDataIntoConfig(data LoggingIntegration, resName string) (string, error) { +func formatLogsIntegrationDataIntoConfig(data logging.LoggingIntegration, resName string) (string, error) { var config string switch { case data.CloudWatch != nil: diff --git a/cyral/resource_cyral_integration_slack_alerts.go b/cyral/internal/integration/slack/resource_cyral_integration_slack_alerts.go similarity index 70% rename from cyral/resource_cyral_integration_slack_alerts.go rename to cyral/internal/integration/slack/resource_cyral_integration_slack_alerts.go index 48a4e514..217c4000 100644 --- a/cyral/resource_cyral_integration_slack_alerts.go +++ b/cyral/internal/integration/slack/resource_cyral_integration_slack_alerts.go @@ -1,10 +1,11 @@ -package cyral +package slack import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -25,43 +26,43 @@ func (data *SlackAlertsIntegration) ReadFromSchema(d *schema.ResourceData) error return nil } -var ReadSlackAlertsConfig = ResourceOperationConfig{ +var ReadSlackAlertsConfig = core.ResourceOperationConfig{ Name: "SlackAlertsResourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/notifications/slack/%s", c.ControlPlane, d.Id()) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &SlackAlertsIntegration{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "Integration Slack"}, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &SlackAlertsIntegration{} }, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Integration Slack"}, } -func resourceIntegrationSlackAlerts() *schema.Resource { +func ResourceIntegrationSlackAlerts() *schema.Resource { return &schema.Resource{ Description: "Manages [integration with Slack to push alerts](https://cyral.com/docs/integrations/messaging/slack).", - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "SlackAlertsResourceCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/notifications/slack", c.ControlPlane) }, - NewResourceData: func() ResourceData { return &SlackAlertsIntegration{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &IDBasedResponse{} }, + NewResourceData: func() core.ResourceData { return &SlackAlertsIntegration{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &core.IDBasedResponse{} }, }, ReadSlackAlertsConfig, ), - ReadContext: ReadResource(ReadSlackAlertsConfig), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadSlackAlertsConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "SlackAlertsResourceUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/notifications/slack/%s", c.ControlPlane, d.Id()) }, - NewResourceData: func() ResourceData { return &SlackAlertsIntegration{} }, + NewResourceData: func() core.ResourceData { return &SlackAlertsIntegration{} }, }, ReadSlackAlertsConfig, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "SlackAlertsResourceDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { diff --git a/cyral/resource_cyral_integration_slack_alerts_test.go b/cyral/internal/integration/slack/resource_cyral_integration_slack_alerts_test.go similarity index 62% rename from cyral/resource_cyral_integration_slack_alerts_test.go rename to cyral/internal/integration/slack/resource_cyral_integration_slack_alerts_test.go index e32779f4..30a3d332 100644 --- a/cyral/resource_cyral_integration_slack_alerts_test.go +++ b/cyral/internal/integration/slack/resource_cyral_integration_slack_alerts_test.go @@ -1,9 +1,12 @@ -package cyral +package slack_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/slack" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -11,13 +14,13 @@ const ( integrationSlackAlertsResourceName = "integration-slack-alerts" ) -var initialSlackAlertsConfig SlackAlertsIntegration = SlackAlertsIntegration{ - Name: accTestName(integrationSlackAlertsResourceName, "slack-alerts"), +var initialSlackAlertsConfig slack.SlackAlertsIntegration = slack.SlackAlertsIntegration{ + Name: utils.AccTestName(integrationSlackAlertsResourceName, "slack-alerts"), URL: "https://slack.local", } -var updatedSlackAlertsConfig SlackAlertsIntegration = SlackAlertsIntegration{ - Name: accTestName(integrationSlackAlertsResourceName, "slack-alerts-updated"), +var updatedSlackAlertsConfig slack.SlackAlertsIntegration = slack.SlackAlertsIntegration{ + Name: utils.AccTestName(integrationSlackAlertsResourceName, "slack-alerts-updated"), URL: "https://slack-updated.local", } @@ -26,7 +29,7 @@ func TestAccSlackAlertsIntegrationResource(t *testing.T) { testUpdateConfig, testUpdateFunc := setupSlackAlertTest(initialSlackAlertsConfig) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -45,7 +48,7 @@ func TestAccSlackAlertsIntegrationResource(t *testing.T) { }) } -func setupSlackAlertTest(integrationData SlackAlertsIntegration) (string, resource.TestCheckFunc) { +func setupSlackAlertTest(integrationData slack.SlackAlertsIntegration) (string, resource.TestCheckFunc) { configuration := formatSlackAlertsIntegrationDataIntoConfig(integrationData) testFunction := resource.ComposeTestCheckFunc( @@ -58,7 +61,7 @@ func setupSlackAlertTest(integrationData SlackAlertsIntegration) (string, resour return configuration, testFunction } -func formatSlackAlertsIntegrationDataIntoConfig(data SlackAlertsIntegration) string { +func formatSlackAlertsIntegrationDataIntoConfig(data slack.SlackAlertsIntegration) string { return fmt.Sprintf(` resource "cyral_integration_slack_alerts" "test_slack_alerts" { name = "%s" diff --git a/cyral/resource_cyral_integration_teams.go b/cyral/internal/integration/teams/resource_cyral_integration_teams.go similarity index 70% rename from cyral/resource_cyral_integration_teams.go rename to cyral/internal/integration/teams/resource_cyral_integration_teams.go index 7a2a3760..1733e186 100644 --- a/cyral/resource_cyral_integration_teams.go +++ b/cyral/internal/integration/teams/resource_cyral_integration_teams.go @@ -1,10 +1,11 @@ -package cyral +package teams import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -25,43 +26,43 @@ func (data *MsTeamsIntegration) ReadFromSchema(d *schema.ResourceData) error { return nil } -var ReadMsTeamsConfig = ResourceOperationConfig{ +var ReadMsTeamsConfig = core.ResourceOperationConfig{ Name: "MsTeamsResourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/notifications/teams/%s", c.ControlPlane, d.Id()) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &MsTeamsIntegration{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "Integration Teams"}, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &MsTeamsIntegration{} }, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Integration Teams"}, } -func resourceIntegrationMsTeams() *schema.Resource { +func ResourceIntegrationMsTeams() *schema.Resource { return &schema.Resource{ Description: "Manages [integration with Microsoft Teams](https://cyral.com/docs/integrations/messaging/microsoft-teams/).", - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "MsTeamsResourceCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/notifications/teams", c.ControlPlane) }, - NewResourceData: func() ResourceData { return &MsTeamsIntegration{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &IDBasedResponse{} }, + NewResourceData: func() core.ResourceData { return &MsTeamsIntegration{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &core.IDBasedResponse{} }, }, ReadMsTeamsConfig, ), - ReadContext: ReadResource(ReadMsTeamsConfig), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadMsTeamsConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "MsTeamsResourceUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/notifications/teams/%s", c.ControlPlane, d.Id()) }, - NewResourceData: func() ResourceData { return &MsTeamsIntegration{} }, + NewResourceData: func() core.ResourceData { return &MsTeamsIntegration{} }, }, ReadMsTeamsConfig, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "MsTeamsResourceDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { diff --git a/cyral/resource_cyral_integration_teams_test.go b/cyral/internal/integration/teams/resource_cyral_integration_teams_test.go similarity index 64% rename from cyral/resource_cyral_integration_teams_test.go rename to cyral/internal/integration/teams/resource_cyral_integration_teams_test.go index 0786e3ca..e53b575f 100644 --- a/cyral/resource_cyral_integration_teams_test.go +++ b/cyral/internal/integration/teams/resource_cyral_integration_teams_test.go @@ -1,9 +1,12 @@ -package cyral +package teams_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/teams" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -11,13 +14,13 @@ const ( integrationTeamsResourceName = "integrations-teams" ) -var initialTeamsConfig MsTeamsIntegration = MsTeamsIntegration{ - Name: accTestName(integrationTeamsResourceName, "msteams-alerts"), +var initialTeamsConfig teams.MsTeamsIntegration = teams.MsTeamsIntegration{ + Name: utils.AccTestName(integrationTeamsResourceName, "msteams-alerts"), URL: "https://msteams.local", } -var updatedTeamsConfig MsTeamsIntegration = MsTeamsIntegration{ - Name: accTestName(integrationTeamsResourceName, "msteams-alerts"), +var updatedTeamsConfig teams.MsTeamsIntegration = teams.MsTeamsIntegration{ + Name: utils.AccTestName(integrationTeamsResourceName, "msteams-alerts"), URL: "https://msteams-updated.local", } @@ -26,7 +29,7 @@ func TestAccMsTeamsIntegrationResource(t *testing.T) { testUpdateConfig, testUpdateFunc := setupTeamsTest(initialTeamsConfig) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -45,7 +48,7 @@ func TestAccMsTeamsIntegrationResource(t *testing.T) { }) } -func setupTeamsTest(integrationData MsTeamsIntegration) (string, resource.TestCheckFunc) { +func setupTeamsTest(integrationData teams.MsTeamsIntegration) (string, resource.TestCheckFunc) { configuration := formatMsTeamsIntegrationDataIntoConfig(integrationData) testFunction := resource.ComposeTestCheckFunc( @@ -58,7 +61,7 @@ func setupTeamsTest(integrationData MsTeamsIntegration) (string, resource.TestCh return configuration, testFunction } -func formatMsTeamsIntegrationDataIntoConfig(data MsTeamsIntegration) string { +func formatMsTeamsIntegrationDataIntoConfig(data teams.MsTeamsIntegration) string { return fmt.Sprintf(` resource "cyral_integration_microsoft_teams" "test_microsoft_teams" { name = "%s" diff --git a/cyral/data_source_cyral_permission.go b/cyral/internal/permission/data_source_cyral_permission.go similarity index 78% rename from cyral/data_source_cyral_permission.go rename to cyral/internal/permission/data_source_cyral_permission.go index 6e093c3b..60db95e0 100644 --- a/cyral/data_source_cyral_permission.go +++ b/cyral/internal/permission/data_source_cyral_permission.go @@ -1,4 +1,4 @@ -package cyral +package permission import ( "fmt" @@ -7,7 +7,9 @@ import ( "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" ) const ( @@ -26,24 +28,24 @@ func (response *PermissionDataSourceResponse) WriteToSchema(d *schema.ResourceDa return nil } -func dataSourcePermission() *schema.Resource { +func DataSourcePermission() *schema.Resource { return &schema.Resource{ Description: "Retrieve all Cyral permissions. See also resource " + "[`cyral_service_account`](../resources/service_account.md).", - ReadContext: ReadResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource( + core.ResourceOperationConfig{ Name: "PermissionDataSourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/users/roles", c.ControlPlane) }, - NewResponseData: func(d *schema.ResourceData) ResponseData { + NewResponseData: func(d *schema.ResourceData) core.ResponseData { return &PermissionDataSourceResponse{} }, }, ), Schema: map[string]*schema.Schema{ - IDKey: { + utils.IDKey: { Description: "The data source identifier.", Type: schema.TypeString, Computed: true, @@ -54,17 +56,17 @@ func dataSourcePermission() *schema.Resource { Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - IDKey: { + utils.IDKey: { Description: "Permission identifier.", Type: schema.TypeString, Computed: true, }, - NameKey: { + utils.NameKey: { Description: "Permission name.", Type: schema.TypeString, Computed: true, }, - DescriptionKey: { + utils.DescriptionKey: { Description: "Permission description.", Type: schema.TypeString, Computed: true, diff --git a/cyral/data_source_cyral_permission_test.go b/cyral/internal/permission/data_source_cyral_permission_test.go similarity index 63% rename from cyral/data_source_cyral_permission_test.go rename to cyral/internal/permission/data_source_cyral_permission_test.go index 844ee130..3690a029 100644 --- a/cyral/data_source_cyral_permission_test.go +++ b/cyral/internal/permission/data_source_cyral_permission_test.go @@ -1,9 +1,12 @@ -package cyral +package permission_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/permission" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -18,7 +21,7 @@ func TestAccPermissionDataSource(t *testing.T) { ) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: testSteps, }) } @@ -30,27 +33,27 @@ func accTestStepPermissionDataSource_RetrieveAllPermissions(dataSourceName strin } `, dataSourceName) var checks []resource.TestCheckFunc - for index, expectedPermissionName := range allPermissionNames { + for index, expectedPermissionName := range permission.AllPermissionNames { checks = append(checks, []resource.TestCheckFunc{ resource.TestCheckResourceAttrSet( dataSourceFullName, fmt.Sprintf( "%s.%d.%s", - PermissionDataSourcePermissionListKey, + permission.PermissionDataSourcePermissionListKey, index, - IDKey, + utils.IDKey, ), ), resource.TestCheckTypeSetElemNestedAttrs( dataSourceFullName, - fmt.Sprintf("%s.*", PermissionDataSourcePermissionListKey), - map[string]string{NameKey: expectedPermissionName}, + fmt.Sprintf("%s.*", permission.PermissionDataSourcePermissionListKey), + map[string]string{utils.NameKey: expectedPermissionName}, ), resource.TestCheckTypeSetElemNestedAttrs( dataSourceFullName, - fmt.Sprintf("%s.*", PermissionDataSourcePermissionListKey), - map[string]string{DescriptionKey: expectedPermissionName}, + fmt.Sprintf("%s.*", permission.PermissionDataSourcePermissionListKey), + map[string]string{utils.DescriptionKey: expectedPermissionName}, ), }..., ) diff --git a/cyral/internal/permission/model_permission.go b/cyral/internal/permission/model_permission.go new file mode 100644 index 00000000..2cc1c1eb --- /dev/null +++ b/cyral/internal/permission/model_permission.go @@ -0,0 +1,39 @@ +package permission + +import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" +) + +type Permission struct { + Id string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` +} + +func permissionsToInterfaceList(permissions []Permission) []any { + permissionsInterfaceList := make([]any, len(permissions)) + for index, permission := range permissions { + permissionsInterfaceList[index] = map[string]any{ + utils.IDKey: permission.Id, + utils.NameKey: permission.Name, + utils.DescriptionKey: permission.Description, + } + } + return permissionsInterfaceList +} + +var AllPermissionNames = []string{ + "Approval Management", + "Modify Policies", + "Modify Roles", + "Modify Sidecars and Repositories", + "Modify Users", + "Repo Crawler", + "View Audit Logs", + "View Datamaps", + "View Integrations", + "View Policies", + "View Roles", + "View Users", + "Modify Integrations", +} diff --git a/cyral/model_policy.go b/cyral/internal/policy/model_policy.go similarity index 97% rename from cyral/model_policy.go rename to cyral/internal/policy/model_policy.go index 46d9cc14..500dda8f 100644 --- a/cyral/model_policy.go +++ b/cyral/internal/policy/model_policy.go @@ -1,4 +1,4 @@ -package cyral +package policy import ( "time" diff --git a/cyral/resource_cyral_policy.go b/cyral/internal/policy/resource_cyral_policy.go similarity index 77% rename from cyral/resource_cyral_policy.go rename to cyral/internal/policy/resource_cyral_policy.go index 753b2d07..46e75be9 100644 --- a/cyral/resource_cyral_policy.go +++ b/cyral/internal/policy/resource_cyral_policy.go @@ -1,4 +1,4 @@ -package cyral +package policy import ( "context" @@ -7,12 +7,14 @@ import ( "log" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -func resourcePolicy() *schema.Resource { +func ResourcePolicy() *schema.Resource { return &schema.Resource{ Description: "Manages [policies](https://cyral.com/docs/reference/policy). See also: " + "[Policy Rule](./policy_rule.md). For more information, see the " + @@ -104,7 +106,7 @@ func resourcePolicy() *schema.Resource { CustomizeDiff: func(ctx context.Context, resourceDiff *schema.ResourceDiff, i interface{}) error { computedKeysToChange := []string{"last_updated", "version"} - setKeysAsNewComputedIfPlanHasChanges(resourceDiff, computedKeysToChange) + utils.SetKeysAsNewComputedIfPlanHasChanges(resourceDiff, computedKeysToChange) return nil }, @@ -125,12 +127,12 @@ func resourcePolicyCreate(ctx context.Context, d *schema.ResourceData, m interfa body, err := c.DoRequest(url, http.MethodPost, policy) if err != nil { - return createError("Unable to create policy", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to create policy", fmt.Sprintf("%v", err)) } - response := IDBasedResponse{} + response := core.IDBasedResponse{} if err := json.Unmarshal(body, &response); err != nil { - return createError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] Response body (unmarshalled): %#v", response) @@ -149,12 +151,12 @@ func resourcePolicyRead(ctx context.Context, d *schema.ResourceData, m interface body, err := c.DoRequest(url, http.MethodGet, nil) if err != nil { - return createError("Unable to read policy", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to read policy", fmt.Sprintf("%v", err)) } response := Policy{} if err := json.Unmarshal(body, &response); err != nil { - return createError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] Response body (unmarshalled): %#v", response) @@ -191,7 +193,7 @@ func resourcePolicyUpdate(ctx context.Context, d *schema.ResourceData, m interfa _, err := c.DoRequest(url, http.MethodPut, policy) if err != nil { - return createError("Unable to update policy", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to update policy", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] End resourcePolicyUpdate") @@ -206,7 +208,7 @@ func resourcePolicyDelete(ctx context.Context, d *schema.ResourceData, m interfa url := fmt.Sprintf("https://%s/v1/policies/%s", c.ControlPlane, d.Id()) if _, err := c.DoRequest(url, http.MethodDelete, nil); err != nil { - return createError("Unable to delete policy", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to delete policy", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] End resourcePolicyDelete") @@ -214,22 +216,12 @@ func resourcePolicyDelete(ctx context.Context, d *schema.ResourceData, m interfa return diag.Diagnostics{} } -func getStrListFromSchemaField(d *schema.ResourceData, field string) []string { - strList := []string{} - - for _, v := range d.Get(field).([]interface{}) { - strList = append(strList, v.(string)) - } - - return strList -} - func getPolicyInfoFromResource(d *schema.ResourceData) Policy { - data := getStrListFromSchemaField(d, "data") - dataTags := getStrListFromSchemaField(d, "data_label_tags") - metadataTags := getStrListFromSchemaField(d, "metadata_tags") + data := utils.GetStrListFromSchemaField(d, "data") + dataTags := utils.GetStrListFromSchemaField(d, "data_label_tags") + metadataTags := utils.GetStrListFromSchemaField(d, "metadata_tags") if len(metadataTags) == 0 { - metadataTags = getStrListFromSchemaField(d, "tags") + metadataTags = utils.GetStrListFromSchemaField(d, "tags") } policy := Policy{ @@ -262,3 +254,40 @@ func getPolicyInfoFromResource(d *schema.ResourceData) Policy { return policy } + +func ListPolicies(c *client.Client) ([]Policy, error) { + log.Printf("[DEBUG] Init ListPolicies") + + url := fmt.Sprintf("https://%s/v1/policies", c.ControlPlane) + resp, err := c.DoRequest(url, http.MethodGet, nil) + if err != nil { + return nil, err + } + + var listResp PolicyListResponse + if err := json.Unmarshal(resp, &listResp); err != nil { + return nil, err + } + log.Printf("[DEBUG] Response body (unmarshalled): %#v", listResp) + + var policies []Policy + for _, policyID := range listResp.Policies { + url := fmt.Sprintf("https://%s/v1/policies/%s", + c.ControlPlane, policyID) + resp, err := c.DoRequest(url, http.MethodGet, nil) + if err != nil { + return nil, err + } + + var policy Policy + if err := json.Unmarshal(resp, &policy); err != nil { + return nil, err + } + log.Printf("[DEBUG] Response body (unmarshalled): %#v", policy) + + policies = append(policies, policy) + } + + log.Printf("[DEBUG] End ListPolicies") + return policies, nil +} diff --git a/cyral/resource_cyral_policy_test.go b/cyral/internal/policy/resource_cyral_policy_test.go similarity index 75% rename from cyral/resource_cyral_policy_test.go rename to cyral/internal/policy/resource_cyral_policy_test.go index fd7e84f7..b0f7bc11 100644 --- a/cyral/resource_cyral_policy_test.go +++ b/cyral/internal/policy/resource_cyral_policy_test.go @@ -1,19 +1,18 @@ -package cyral +package policy_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/policy" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) -const ( - policyResourceName = "policy" -) - -var initialPolicyConfig = Policy{ - Meta: &PolicyMetadata{ - Name: accTestName(policyResourceName, "test"), +var initialPolicyConfig = policy.Policy{ + Meta: &policy.PolicyMetadata{ + Name: utils.AccTestName(utils.PolicyResourceName, "test"), Description: "description", Enabled: false, Tags: []string{"tag"}, @@ -22,9 +21,9 @@ var initialPolicyConfig = Policy{ Tags: []string{"DATA_TAG_TEST"}, } -var updatedPolicyConfig = Policy{ - Meta: &PolicyMetadata{ - Name: accTestName(policyResourceName, "test-updated"), +var updatedPolicyConfig = policy.Policy{ + Meta: &policy.PolicyMetadata{ + Name: utils.AccTestName(utils.PolicyResourceName, "test-updated"), Description: "desctiption-updated", Enabled: true, Tags: []string{"tag-updated"}, @@ -38,7 +37,7 @@ func TestAccPolicyResource(t *testing.T) { testUpdateConfig, testUpdateFunc := setupPolicyTest(updatedPolicyConfig) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -57,7 +56,7 @@ func TestAccPolicyResource(t *testing.T) { }) } -func setupPolicyTest(integrationData Policy) (string, resource.TestCheckFunc) { +func setupPolicyTest(integrationData policy.Policy) (string, resource.TestCheckFunc) { configuration := formatPolicyTestConfigIntoConfig(integrationData) testFunction := resource.ComposeTestCheckFunc( @@ -102,7 +101,7 @@ func setupPolicyTest(integrationData Policy) (string, resource.TestCheckFunc) { return configuration, testFunction } -func formatPolicyTestConfigIntoConfig(data Policy) string { +func formatPolicyTestConfigIntoConfig(data policy.Policy) string { return fmt.Sprintf(` resource "cyral_policy" "policy_test" { name = "%s" @@ -115,8 +114,8 @@ func formatPolicyTestConfigIntoConfig(data Policy) string { data.Meta.Name, data.Meta.Description, data.Meta.Enabled, - listToStr(data.Data), - listToStr(data.Tags), - listToStr(data.Meta.Tags), + utils.ListToStr(data.Data), + utils.ListToStr(data.Tags), + utils.ListToStr(data.Meta.Tags), ) } diff --git a/cyral/resource_cyral_policy_rule.go b/cyral/internal/policy/rule/resource_cyral_policy_rule.go similarity index 91% rename from cyral/resource_cyral_policy_rule.go rename to cyral/internal/policy/rule/resource_cyral_policy_rule.go index 58e704e0..2fbf7249 100644 --- a/cyral/resource_cyral_policy_rule.go +++ b/cyral/internal/policy/rule/resource_cyral_policy_rule.go @@ -1,4 +1,4 @@ -package cyral +package rule import ( "context" @@ -8,7 +8,9 @@ import ( "net/http" "strings" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -51,7 +53,7 @@ func unmarshalPolicyRuleID(d *schema.ResourceData) (policyID string, policyRuleI // v1. In v0, there exists only one field (the policy rule // ID). Therefore, if we assume there are two, the first `terraform // refresh` done when upgrading will fail. - ids, err := unmarshalComposedID(d.Id(), "/", 2) + ids, err := utils.UnMarshalComposedID(d.Id(), "/", 2) if err == nil { // This is the new way to organize the IDs (v1). policyID = ids[0] @@ -232,7 +234,7 @@ func policyRuleResourceSchemaV0() *schema.Resource { // `terraform import` and the computed id for the resource. The goal of this // upgrade is to set the `id` attribute to have the format // `{policy_id}/{policy_rule_id}`. -func upgradePolicyRuleV0( +func UpgradePolicyRuleV0( _ context.Context, rawState map[string]interface{}, _ interface{}, @@ -248,13 +250,13 @@ func upgradePolicyRuleV0( } policyID := rawState["policy_id"].(string) - newID := marshalComposedID([]string{policyID, policyRuleID}, "/") + newID := utils.MarshalComposedID([]string{policyID, policyRuleID}, "/") rawState["id"] = newID return rawState, nil } -func resourcePolicyRule() *schema.Resource { +func ResourcePolicyRule() *schema.Resource { return &schema.Resource{ Description: "Manages [policy rules](https://cyral.com/docs/reference/policy/#rules). " + "See also the [`cyral_policy`](https://registry.terraform.io/providers/cyralinc/cyral/latest/docs/resources/policy) " + @@ -267,7 +269,7 @@ func resourcePolicyRule() *schema.Resource { CreateContext: resourcePolicyRuleCreate, ReadContext: resourcePolicyRuleRead, UpdateContext: resourcePolicyRuleUpdate, - DeleteContext: DeleteResource(policyRuleDeleteConfig()), + DeleteContext: core.DeleteResource(policyRuleDeleteConfig()), SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ @@ -275,7 +277,7 @@ func resourcePolicyRule() *schema.Resource { Version: 0, Type: policyRuleResourceSchemaV0(). CoreConfigSchema().ImpliedType(), - Upgrade: upgradePolicyRuleV0, + Upgrade: UpgradePolicyRuleV0, }, }, @@ -287,7 +289,7 @@ func resourcePolicyRule() *schema.Resource { d *schema.ResourceData, m interface{}, ) ([]*schema.ResourceData, error) { - ids, err := unmarshalComposedID(d.Id(), "/", 2) + ids, err := utils.UnMarshalComposedID(d.Id(), "/", 2) if err != nil { return nil, err } @@ -311,16 +313,16 @@ func resourcePolicyRuleCreate(ctx context.Context, d *schema.ResourceData, m int body, err := c.DoRequest(url, http.MethodPost, resourceData) if err != nil { - return createError("Unable to create policy rule", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to create policy rule", fmt.Sprintf("%v", err)) } - response := IDBasedResponse{} + response := core.IDBasedResponse{} if err := json.Unmarshal(body, &response); err != nil { - return createError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] Response body (unmarshalled): %#v", response) - d.SetId(marshalComposedID([]string{ + d.SetId(utils.MarshalComposedID([]string{ policyID, response.ID}, "/")) @@ -340,31 +342,31 @@ func resourcePolicyRuleRead(ctx context.Context, d *schema.ResourceData, m inter body, err := c.DoRequest(url, http.MethodGet, nil) if err != nil { - return createError("Unable to read policy rule", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to read policy rule", fmt.Sprintf("%v", err)) } response := PolicyRule{} if err := json.Unmarshal(body, &response); err != nil { - return createError(fmt.Sprintf("Unable to unmarshall JSON"), fmt.Sprintf("%v", err)) + return utils.CreateError(fmt.Sprintf("Unable to unmarshall JSON"), fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] Response body (unmarshalled): %#v", response) deletes := flattenRulesList(response.Deletes) log.Printf("[DEBUG] flattened deletes %#v", deletes) if err := d.Set("deletes", deletes); err != nil { - return createError("Unable to read policy rule", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to read policy rule", fmt.Sprintf("%v", err)) } reads := flattenRulesList(response.Reads) log.Printf("[DEBUG] flattened reads %#v", reads) if err := d.Set("reads", reads); err != nil { - return createError("Unable to read policy rule", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to read policy rule", fmt.Sprintf("%v", err)) } updates := flattenRulesList(response.Updates) log.Printf("[DEBUG] flattened updates %#v", updates) if err := d.Set("updates", updates); err != nil { - return createError("Unable to read policy rule", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to read policy rule", fmt.Sprintf("%v", err)) } if response.Identities != nil { @@ -373,7 +375,7 @@ func resourcePolicyRuleRead(ctx context.Context, d *schema.ResourceData, m inter identities := flattenIdentities(response.Identities) log.Printf("[DEBUG] flattened identities %#v", identities) if err := d.Set("identities", identities); err != nil { - return createError("Unable to read policy rule", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to read policy rule", fmt.Sprintf("%v", err)) } } } @@ -399,7 +401,7 @@ func resourcePolicyRuleUpdate(ctx context.Context, d *schema.ResourceData, m int _, err := c.DoRequest(url, http.MethodPut, policyRule) if err != nil { - return createError("Unable to update policy rule", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to update policy rule", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] End resourcePolicyRuleUpdate") @@ -407,8 +409,8 @@ func resourcePolicyRuleUpdate(ctx context.Context, d *schema.ResourceData, m int return resourcePolicyRuleRead(ctx, d, m) } -func policyRuleDeleteConfig() ResourceOperationConfig { - return ResourceOperationConfig{ +func policyRuleDeleteConfig() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "PolicyRuleDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -416,7 +418,7 @@ func policyRuleDeleteConfig() ResourceOperationConfig { return fmt.Sprintf("https://%s/v1/policies/%s/rules/%s", c.ControlPlane, policyID, policyRuleID) }, - RequestErrorHandler: &DeleteIgnoreHttpNotFound{resName: "Policy Rule"}, + RequestErrorHandler: &core.DeleteIgnoreHttpNotFound{ResName: "Policy Rule"}, } } @@ -483,7 +485,7 @@ func getRuleListFromResource(d *schema.ResourceData, name string) []Rule { func getPolicyRuleInfoFromResource(d *schema.ResourceData) PolicyRule { log.Printf("[DEBUG] Init getPolicyRuleInfoFromResource") - hosts := getStrListFromSchemaField(d, "hosts") + hosts := utils.GetStrListFromSchemaField(d, "hosts") identity := d.Get("identities").([]interface{}) diff --git a/cyral/resource_cyral_policy_rule_test.go b/cyral/internal/policy/rule/resource_cyral_policy_rule_test.go similarity index 88% rename from cyral/resource_cyral_policy_rule_test.go rename to cyral/internal/policy/rule/resource_cyral_policy_rule_test.go index a8009d06..37530ca0 100644 --- a/cyral/resource_cyral_policy_rule_test.go +++ b/cyral/internal/policy/rule/resource_cyral_policy_rule_test.go @@ -1,10 +1,13 @@ -package cyral +package rule_test import ( "context" "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/policy/rule" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/stretchr/testify/require" ) @@ -20,7 +23,7 @@ type PolicyRuleConfig struct { DeletedRateLimit int UpdatedRateLimit int ReadRateLimit int - Identities *Identity + Identities *rule.Identity } var initialPolicyRuleConfig PolicyRuleConfig = PolicyRuleConfig{ @@ -30,7 +33,7 @@ var initialPolicyRuleConfig PolicyRuleConfig = PolicyRuleConfig{ DeletedRateLimit: 1, UpdatedRateLimit: 2, ReadRateLimit: 3, - Identities: &Identity{ + Identities: &rule.Identity{ DBRoles: []string{ "db-role-1", }, @@ -44,7 +47,7 @@ var updatedGroupsPolicyRuleConfig PolicyRuleConfig = PolicyRuleConfig{ DeletedRateLimit: 2, UpdatedRateLimit: 3, ReadRateLimit: 4, - Identities: &Identity{ + Identities: &rule.Identity{ Groups: []string{ "groups-1", }, @@ -58,7 +61,7 @@ var updatedServicesPolicyRuleConfig PolicyRuleConfig = PolicyRuleConfig{ DeletedRateLimit: 5, UpdatedRateLimit: 6, ReadRateLimit: 7, - Identities: &Identity{ + Identities: &rule.Identity{ Services: []string{ "services-1", }, @@ -72,7 +75,7 @@ var updatedUsersPolicyRuleConfig PolicyRuleConfig = PolicyRuleConfig{ DeletedRateLimit: 5, UpdatedRateLimit: 6, ReadRateLimit: 7, - Identities: &Identity{ + Identities: &rule.Identity{ Users: []string{ "users-1", }, @@ -98,7 +101,7 @@ func TestAccPolicyRuleResource(t *testing.T) { importStateResName := "cyral_policy_rule.policy_rule_test" resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -134,7 +137,7 @@ func TestPolicyRuleResourceUpgradeV0(t *testing.T) { "id": "policy-rule-id", "policy_id": "policy-id", } - actualNewState, err := upgradePolicyRuleV0(context.Background(), + actualNewState, err := rule.UpgradePolicyRuleV0(context.Background(), previousState, nil) require.NoError(t, err) expectedNewState := map[string]interface{}{ @@ -147,8 +150,8 @@ func TestPolicyRuleResourceUpgradeV0(t *testing.T) { func setupPolicyRuleTest(policyRule PolicyRuleConfig) (string, resource.TestCheckFunc) { testLabelName := "TEST_CCN" var config string - config += formatBasicPolicyIntoConfig( - accTestName(policyRuleResourceName, "policy"), + config += utils.FormatBasicPolicyIntoConfig( + utils.AccTestName(policyRuleResourceName, "policy"), []string{testLabelName}, ) config += formatPolicyRuleConfigIntoConfig( @@ -210,19 +213,19 @@ func formatPolicyRuleConfigIntoConfig( if identities.DBRoles != nil { identitiesStr += fmt.Sprintf(` - db_roles = %s`, listToStr(identities.DBRoles)) + db_roles = %s`, utils.ListToStr(identities.DBRoles)) } if identities.Groups != nil { identitiesStr += fmt.Sprintf(` - groups = %s`, listToStr(identities.Groups)) + groups = %s`, utils.ListToStr(identities.Groups)) } if identities.Services != nil { identitiesStr += fmt.Sprintf(` - services = %s`, listToStr(identities.Services)) + services = %s`, utils.ListToStr(identities.Services)) } if identities.Users != nil { identitiesStr += fmt.Sprintf(` - users = %s`, listToStr(identities.Users)) + users = %s`, utils.ListToStr(identities.Users)) } identitiesStr += ` diff --git a/cyral/model_rego_policy_instance.go b/cyral/internal/regopolicy/model_rego_policy_instance.go similarity index 67% rename from cyral/model_rego_policy_instance.go rename to cyral/internal/regopolicy/model_rego_policy_instance.go index a4e398ff..080352e1 100644 --- a/cyral/model_rego_policy_instance.go +++ b/cyral/internal/regopolicy/model_rego_policy_instance.go @@ -1,6 +1,7 @@ -package cyral +package regopolicy import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -10,14 +11,14 @@ type RegoPolicyInstancePayload struct { } func (policy *RegoPolicyInstancePayload) ReadFromSchema(d *schema.ResourceData) error { - policy.RegoPolicyInstance.Name = d.Get(regoPolicyInstanceNameKey).(string) - policy.RegoPolicyInstance.Description = d.Get(regoPolicyInstanceDescriptionKey).(string) - policy.RegoPolicyInstance.TemplateID = d.Get(regoPolicyInstanceTemplateIDKey).(string) - policy.RegoPolicyInstance.Parameters = d.Get(regoPolicyInstanceParametersKey).(string) - policy.RegoPolicyInstance.Enabled = d.Get(regoPolicyInstanceEnabledKey).(bool) - policy.RegoPolicyInstance.Scope = NewScopeFromInterface(d.Get(regoPolicyInstanceScopeKey)) - policy.RegoPolicyInstance.TagsFromInterfaceList(d.Get(regoPolicyInstanceTagsKey).([]any)) - policy.Duration = d.Get(regoPolicyInstanceDurationKey).(string) + policy.RegoPolicyInstance.Name = d.Get(RegoPolicyInstanceNameKey).(string) + policy.RegoPolicyInstance.Description = d.Get(RegoPolicyInstanceDescriptionKey).(string) + policy.RegoPolicyInstance.TemplateID = d.Get(RegoPolicyInstanceTemplateIDKey).(string) + policy.RegoPolicyInstance.Parameters = d.Get(RegoPolicyInstanceParametersKey).(string) + policy.RegoPolicyInstance.Enabled = d.Get(RegoPolicyInstanceEnabledKey).(bool) + policy.RegoPolicyInstance.Scope = NewScopeFromInterface(d.Get(RegoPolicyInstanceScopeKey)) + policy.RegoPolicyInstance.TagsFromInterfaceList(d.Get(RegoPolicyInstanceTagsKey).([]any)) + policy.Duration = d.Get(RegoPolicyInstanceDurationKey).(string) return nil } @@ -34,15 +35,15 @@ type RegoPolicyInstance struct { } func (policy *RegoPolicyInstance) WriteToSchema(d *schema.ResourceData) error { - d.Set(regoPolicyInstanceNameKey, policy.Name) - d.Set(regoPolicyInstanceDescriptionKey, policy.Description) - d.Set(regoPolicyInstanceTemplateIDKey, policy.TemplateID) - d.Set(regoPolicyInstanceParametersKey, policy.Parameters) - d.Set(regoPolicyInstanceEnabledKey, policy.Enabled) - d.Set(regoPolicyInstanceScopeKey, policy.Scope.ToInterfaceList()) - d.Set(regoPolicyInstanceTagsKey, policy.TagsToInterfaceList()) - d.Set(regoPolicyInstanceLastUpdatedKey, policy.LastUpdated.ToInterfaceList()) - d.Set(regoPolicyInstanceCreatedKey, policy.Created.ToInterfaceList()) + d.Set(RegoPolicyInstanceNameKey, policy.Name) + d.Set(RegoPolicyInstanceDescriptionKey, policy.Description) + d.Set(RegoPolicyInstanceTemplateIDKey, policy.TemplateID) + d.Set(RegoPolicyInstanceParametersKey, policy.Parameters) + d.Set(RegoPolicyInstanceEnabledKey, policy.Enabled) + d.Set(RegoPolicyInstanceScopeKey, policy.Scope.ToInterfaceList()) + d.Set(RegoPolicyInstanceTagsKey, policy.TagsToInterfaceList()) + d.Set(RegoPolicyInstanceLastUpdatedKey, policy.LastUpdated.ToInterfaceList()) + d.Set(RegoPolicyInstanceCreatedKey, policy.Created.ToInterfaceList()) return nil } @@ -78,7 +79,7 @@ func NewScopeFromInterface(scopeInterface any) *RegoPolicyInstanceScope { return nil } scopeMap := scopeInterfaceList[0].(map[string]any) - repoIDsInterfaceList := scopeMap[regoPolicyInstanceRepoIDsKey].([]any) + repoIDsInterfaceList := scopeMap[RegoPolicyInstanceRepoIDsKey].([]any) repoIDs := make([]string, len(repoIDsInterfaceList)) for index, repoID := range repoIDsInterfaceList { repoIDs[index] = repoID.(string) @@ -94,7 +95,7 @@ func (scope *RegoPolicyInstanceScope) ToInterfaceList() []any { } return []any{ map[string]any{ - regoPolicyInstanceRepoIDsKey: scope.RepoIDsToInterfaceList(), + RegoPolicyInstanceRepoIDsKey: scope.RepoIDsToInterfaceList(), }, } } @@ -122,9 +123,9 @@ func (changeInfo *RegoPolicyInstanceChangeInfo) ToInterfaceList() []any { } return []any{ map[string]any{ - regoPolicyInstanceActorKey: changeInfo.Actor, - regoPolicyInstanceActorTypeKey: changeInfo.ActorType, - regoPolicyInstanceTimestampKey: changeInfo.Timestamp, + RegoPolicyInstanceActorKey: changeInfo.Actor, + RegoPolicyInstanceActorTypeKey: changeInfo.ActorType, + RegoPolicyInstanceTimestampKey: changeInfo.Timestamp, }, } } @@ -135,7 +136,7 @@ type RegoPolicyInstanceKey struct { } func (key RegoPolicyInstanceKey) WriteToSchema(d *schema.ResourceData) error { - d.SetId(marshalComposedID([]string{key.Category, key.ID}, "/")) - d.Set(regoPolicyInstancePolicyIDKey, key.ID) + d.SetId(utils.MarshalComposedID([]string{key.Category, key.ID}, "/")) + d.Set(RegoPolicyInstancePolicyIDKey, key.ID) return nil } diff --git a/cyral/resource_cyral_rego_policy_instance.go b/cyral/internal/regopolicy/resource_cyral_rego_policy_instance.go similarity index 65% rename from cyral/resource_cyral_rego_policy_instance.go rename to cyral/internal/regopolicy/resource_cyral_rego_policy_instance.go index 09c6b962..a56f5af9 100644 --- a/cyral/resource_cyral_rego_policy_instance.go +++ b/cyral/internal/regopolicy/resource_cyral_rego_policy_instance.go @@ -1,67 +1,69 @@ -package cyral +package regopolicy import ( "context" "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) const ( // Schema keys - regoPolicyInstanceResourceIDKey = "id" - regoPolicyInstancePolicyIDKey = "policy_id" - regoPolicyInstanceCategoryKey = "category" - regoPolicyInstanceNameKey = "name" - regoPolicyInstanceDescriptionKey = "description" - regoPolicyInstanceTemplateIDKey = "template_id" - regoPolicyInstanceParametersKey = "parameters" - regoPolicyInstanceEnabledKey = "enabled" - regoPolicyInstanceScopeKey = "scope" - regoPolicyInstanceRepoIDsKey = "repo_ids" - regoPolicyInstanceTagsKey = "tags" - regoPolicyInstanceDurationKey = "duration" - regoPolicyInstanceLastUpdatedKey = "last_updated" - regoPolicyInstanceCreatedKey = "created" - regoPolicyInstanceActorKey = "actor" - regoPolicyInstanceActorTypeKey = "actor_type" - regoPolicyInstanceTimestampKey = "timestamp" + RegoPolicyInstanceResourceIDKey = "id" + RegoPolicyInstancePolicyIDKey = "policy_id" + RegoPolicyInstanceCategoryKey = "category" + RegoPolicyInstanceNameKey = "name" + RegoPolicyInstanceDescriptionKey = "description" + RegoPolicyInstanceTemplateIDKey = "template_id" + RegoPolicyInstanceParametersKey = "parameters" + RegoPolicyInstanceEnabledKey = "enabled" + RegoPolicyInstanceScopeKey = "scope" + RegoPolicyInstanceRepoIDsKey = "repo_ids" + RegoPolicyInstanceTagsKey = "tags" + RegoPolicyInstanceDurationKey = "duration" + RegoPolicyInstanceLastUpdatedKey = "last_updated" + RegoPolicyInstanceCreatedKey = "created" + RegoPolicyInstanceActorKey = "actor" + RegoPolicyInstanceActorTypeKey = "actor_type" + RegoPolicyInstanceTimestampKey = "timestamp" ) var ( - ReadRegoPolicyInstanceConfig = ResourceOperationConfig{ + ReadRegoPolicyInstanceConfig = core.ResourceOperationConfig{ Name: "RegoPolicyInstanceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf( "https://%s/v1/regopolicies/instances/%s/%s", c.ControlPlane, - d.Get(regoPolicyInstanceCategoryKey), - d.Get(regoPolicyInstancePolicyIDKey), + d.Get(RegoPolicyInstanceCategoryKey), + d.Get(RegoPolicyInstancePolicyIDKey), ) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &RegoPolicyInstance{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "Rego policy instance"}, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Rego policy instance"}, } regoPolicyChangeInformation = &schema.Resource{ Schema: map[string]*schema.Schema{ - regoPolicyInstanceActorKey: { + RegoPolicyInstanceActorKey: { Description: "Actor that performed the event.", Type: schema.TypeString, Computed: true, }, - regoPolicyInstanceActorTypeKey: { - Description: "Actor type. Valid types are:" + supportedTypesMarkdown(actorTypes()), + RegoPolicyInstanceActorTypeKey: { + Description: "Actor type. Valid types are:" + utils.SupportedValuesAsMarkdown(actorTypes()), Type: schema.TypeString, Computed: true, }, - regoPolicyInstanceTimestampKey: { + RegoPolicyInstanceTimestampKey: { Description: "Timestamp that the event happened.", Type: schema.TypeString, Computed: true, @@ -70,120 +72,120 @@ var ( } ) -func resourceRegoPolicyInstance() *schema.Resource { +func ResourceRegoPolicyInstance() *schema.Resource { return &schema.Resource{ Description: "Manages a [Rego Policy](https://cyral.com/docs/policy/rego-policy/overview#) instance." + "\n\n-> **Note** This resource can be used to create repo-level policies by specifying the repo IDs " + "associated to the policy `scope`. For more information, see the [scope](#nestedblock--scope) field.", - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "RegoPolicyInstanceCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf( "https://%s/v1/regopolicies/instances/%s", c.ControlPlane, - d.Get(regoPolicyInstanceCategoryKey), + d.Get(RegoPolicyInstanceCategoryKey), ) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return &RegoPolicyInstancePayload{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &RegoPolicyInstanceKey{} }, }, ReadRegoPolicyInstanceConfig, ), - ReadContext: ReadResource(ReadRegoPolicyInstanceConfig), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadRegoPolicyInstanceConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "RegoPolicyInstanceUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf( "https://%s/v1/regopolicies/instances/%s/%s", c.ControlPlane, - d.Get(regoPolicyInstanceCategoryKey), - d.Get(regoPolicyInstancePolicyIDKey), + d.Get(RegoPolicyInstanceCategoryKey), + d.Get(RegoPolicyInstancePolicyIDKey), ) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return &RegoPolicyInstancePayload{} }, }, ReadRegoPolicyInstanceConfig, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "RegoPolicyInstanceDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf( "https://%s/v1/regopolicies/instances/%s/%s", c.ControlPlane, - d.Get(regoPolicyInstanceCategoryKey), - d.Get(regoPolicyInstancePolicyIDKey), + d.Get(RegoPolicyInstanceCategoryKey), + d.Get(RegoPolicyInstancePolicyIDKey), ) }, }, ), Schema: map[string]*schema.Schema{ - regoPolicyInstanceResourceIDKey: { + RegoPolicyInstanceResourceIDKey: { Description: "The resource identifier. It is a composed ID that follows the format `{category}/{policy_id}`.", Type: schema.TypeString, Computed: true, }, - regoPolicyInstancePolicyIDKey: { + RegoPolicyInstancePolicyIDKey: { Description: "ID of this rego policy instance in Cyral environment.", Type: schema.TypeString, Computed: true, }, - regoPolicyInstanceCategoryKey: { + RegoPolicyInstanceCategoryKey: { Description: "Policy category. List of supported categories:" + - supportedTypesMarkdown(regoPolicyCategories()), + utils.SupportedValuesAsMarkdown(regoPolicyCategories()), Type: schema.TypeString, Required: true, ForceNew: true, ValidateFunc: validation.StringInSlice(regoPolicyCategories(), false), }, - regoPolicyInstanceNameKey: { + RegoPolicyInstanceNameKey: { Description: "Policy name.", Type: schema.TypeString, Required: true, }, - regoPolicyInstanceDescriptionKey: { + RegoPolicyInstanceDescriptionKey: { Description: "Policy description.", Type: schema.TypeString, Optional: true, }, - regoPolicyInstanceTemplateIDKey: { + RegoPolicyInstanceTemplateIDKey: { Description: "Policy template identifier. Predefined templates are:" + - supportedTypesMarkdown(regoPolicyTemplateIDs()), + utils.SupportedValuesAsMarkdown(regoPolicyTemplateIDs()), Type: schema.TypeString, Required: true, }, - regoPolicyInstanceParametersKey: { + RegoPolicyInstanceParametersKey: { Description: "Policy parameters. The parameters vary based on the policy template schema.", Type: schema.TypeString, Optional: true, }, - regoPolicyInstanceEnabledKey: { + RegoPolicyInstanceEnabledKey: { Description: "Enable/disable the policy. Defaults to `false` (Disabled).", Type: schema.TypeBool, Optional: true, }, - regoPolicyInstanceScopeKey: { + RegoPolicyInstanceScopeKey: { Description: fmt.Sprintf("Determines the scope that the policy applies to. It can be used to create "+ "a repo-level policy by specifying the corresponding `%s` that this policy should be applied.", - regoPolicyInstanceRepoIDsKey), + RegoPolicyInstanceRepoIDsKey), Type: schema.TypeSet, Optional: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - regoPolicyInstanceRepoIDsKey: { + RegoPolicyInstanceRepoIDsKey: { Description: "A list of repository identifiers that belongs to the policy scope. The policy " + "will be applied at repo-level for every repository ID included in this list. This is equivalent " + "of creating a repo-level policy in the UI for a given repository.", @@ -196,7 +198,7 @@ func resourceRegoPolicyInstance() *schema.Resource { }, }, }, - regoPolicyInstanceTagsKey: { + RegoPolicyInstanceTagsKey: { Description: "Tags that can be used to categorize the policy.", Type: schema.TypeList, Optional: true, @@ -204,21 +206,21 @@ func resourceRegoPolicyInstance() *schema.Resource { Type: schema.TypeString, }, }, - regoPolicyInstanceDurationKey: { + RegoPolicyInstanceDurationKey: { Description: "Policy duration. The policy expires after the duration specified. Should follow the protobuf " + "duration string format, which corresponds to a sequence of decimal numbers suffixed by a 's' at the " + "end, representing the duration in seconds. For example: `300s`, `60s`, `10.50s` etc", Type: schema.TypeString, Optional: true, - ValidateFunc: validationDurationString, + ValidateFunc: utils.ValidationDurationString, }, - regoPolicyInstanceLastUpdatedKey: { + RegoPolicyInstanceLastUpdatedKey: { Description: "Information regarding the policy last update.", Type: schema.TypeSet, Computed: true, Elem: regoPolicyChangeInformation, }, - regoPolicyInstanceCreatedKey: { + RegoPolicyInstanceCreatedKey: { Description: "Information regarding the policy creation.", Type: schema.TypeSet, Computed: true, @@ -227,8 +229,8 @@ func resourceRegoPolicyInstance() *schema.Resource { }, CustomizeDiff: func(ctx context.Context, resourceDiff *schema.ResourceDiff, i interface{}) error { - computedKeysToChange := []string{regoPolicyInstanceLastUpdatedKey} - setKeysAsNewComputedIfPlanHasChanges(resourceDiff, computedKeysToChange) + computedKeysToChange := []string{RegoPolicyInstanceLastUpdatedKey} + utils.SetKeysAsNewComputedIfPlanHasChanges(resourceDiff, computedKeysToChange) return nil }, @@ -238,12 +240,12 @@ func resourceRegoPolicyInstance() *schema.Resource { d *schema.ResourceData, m interface{}, ) ([]*schema.ResourceData, error) { - ids, err := unmarshalComposedID(d.Id(), "/", 2) + ids, err := utils.UnMarshalComposedID(d.Id(), "/", 2) if err != nil { return nil, err } - _ = d.Set(regoPolicyInstanceCategoryKey, ids[0]) - _ = d.Set(regoPolicyInstancePolicyIDKey, ids[1]) + _ = d.Set(RegoPolicyInstanceCategoryKey, ids[0]) + _ = d.Set(RegoPolicyInstancePolicyIDKey, ids[1]) return []*schema.ResourceData{d}, nil }, }, diff --git a/cyral/resource_cyral_rego_policy_instance_test.go b/cyral/internal/regopolicy/resource_cyral_rego_policy_instance_test.go similarity index 74% rename from cyral/resource_cyral_rego_policy_instance_test.go rename to cyral/internal/regopolicy/resource_cyral_rego_policy_instance_test.go index b93c9330..f044c77b 100644 --- a/cyral/resource_cyral_rego_policy_instance_test.go +++ b/cyral/internal/regopolicy/resource_cyral_rego_policy_instance_test.go @@ -1,10 +1,13 @@ -package cyral +package regopolicy_test import ( "fmt" "regexp" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/regopolicy" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -32,7 +35,7 @@ func TestAccRegoPolicyInstanceResource(t *testing.T) { ) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: testSteps, }) } @@ -40,9 +43,9 @@ func TestAccRegoPolicyInstanceResource(t *testing.T) { func getRequiredArgumentTestSteps() []resource.TestStep { requiredArgumentsTestSteps := []resource.TestStep{} requiredArguments := []string{ - regoPolicyInstanceNameKey, - regoPolicyInstanceCategoryKey, - regoPolicyInstanceTemplateIDKey, + regopolicy.RegoPolicyInstanceNameKey, + regopolicy.RegoPolicyInstanceCategoryKey, + regopolicy.RegoPolicyInstanceTemplateIDKey, } for _, argument := range requiredArguments { requiredArgumentsTestSteps = append(requiredArgumentsTestSteps, resource.TestStep{ @@ -56,15 +59,15 @@ func getRequiredArgumentTestSteps() []resource.TestStep { } type RegoPolicyInstanceTestParameters struct { - policy RegoPolicyInstancePayload + policy regopolicy.RegoPolicyInstancePayload policyCategory string isUpdateOperation bool } var ( regoPolicyInstanceOnlyRequiredArguments = RegoPolicyInstanceTestParameters{ - policy: RegoPolicyInstancePayload{ - RegoPolicyInstance: RegoPolicyInstance{ + policy: regopolicy.RegoPolicyInstancePayload{ + RegoPolicyInstance: regopolicy.RegoPolicyInstance{ Name: "some-rate-limit-policy", TemplateID: "rate-limit", Parameters: "{\"rateLimit\":7,\"labels\":[\"EMAIL\"],\"alertSeverity\":\"high\",\"block\":false}", @@ -73,14 +76,14 @@ var ( policyCategory: "SECURITY", } regoPolicyInstanceAllArguments = RegoPolicyInstanceTestParameters{ - policy: RegoPolicyInstancePayload{ - RegoPolicyInstance: RegoPolicyInstance{ + policy: regopolicy.RegoPolicyInstancePayload{ + RegoPolicyInstance: regopolicy.RegoPolicyInstance{ Name: "some-rate-limit-policy", Description: "Some description.", TemplateID: "rate-limit", Parameters: "{\"rateLimit\":7,\"labels\":[\"EMAIL\"],\"alertSeverity\":\"high\",\"block\":false}", Enabled: true, - Scope: &RegoPolicyInstanceScope{ + Scope: ®opolicy.RegoPolicyInstanceScope{ RepoIDs: []string{"2U4prk5o6yi1rTvvXyImz8lgbgG"}, }, Tags: []string{"tag1", "tag2"}, @@ -142,8 +145,8 @@ func testAccRegoPolicyInstanceConfig_AllArguments( parameters.policy.RegoPolicyInstance.TemplateID, parameters.policy.RegoPolicyInstance.Parameters, parameters.policy.RegoPolicyInstance.Enabled, - listToStr(parameters.policy.RegoPolicyInstance.Scope.RepoIDs), - listToStr(parameters.policy.RegoPolicyInstance.Tags), + utils.ListToStr(parameters.policy.RegoPolicyInstance.Scope.RepoIDs), + utils.ListToStr(parameters.policy.RegoPolicyInstance.Tags), parameters.policy.Duration, ) } @@ -153,45 +156,45 @@ func testAccRegoPolicyInstanceCheck( ) resource.TestCheckFunc { testCheckFuncs := []resource.TestCheckFunc{ resource.TestCheckResourceAttrSet("cyral_rego_policy_instance.policy_1", - regoPolicyInstanceResourceIDKey), + regopolicy.RegoPolicyInstanceResourceIDKey), resource.TestCheckResourceAttrSet("cyral_rego_policy_instance.policy_1", - regoPolicyInstancePolicyIDKey), + regopolicy.RegoPolicyInstancePolicyIDKey), resource.TestCheckResourceAttr("cyral_rego_policy_instance.policy_1", - regoPolicyInstanceCategoryKey, parameters.policyCategory), + regopolicy.RegoPolicyInstanceCategoryKey, parameters.policyCategory), resource.TestCheckResourceAttr("cyral_rego_policy_instance.policy_1", - regoPolicyInstanceNameKey, parameters.policy.RegoPolicyInstance.Name), + regopolicy.RegoPolicyInstanceNameKey, parameters.policy.RegoPolicyInstance.Name), resource.TestCheckResourceAttr("cyral_rego_policy_instance.policy_1", - regoPolicyInstanceDescriptionKey, parameters.policy.RegoPolicyInstance.Description), + regopolicy.RegoPolicyInstanceDescriptionKey, parameters.policy.RegoPolicyInstance.Description), resource.TestCheckResourceAttr("cyral_rego_policy_instance.policy_1", - regoPolicyInstanceTemplateIDKey, parameters.policy.RegoPolicyInstance.TemplateID), + regopolicy.RegoPolicyInstanceTemplateIDKey, parameters.policy.RegoPolicyInstance.TemplateID), resource.TestCheckResourceAttr("cyral_rego_policy_instance.policy_1", - regoPolicyInstanceParametersKey, parameters.policy.RegoPolicyInstance.Parameters), + regopolicy.RegoPolicyInstanceParametersKey, parameters.policy.RegoPolicyInstance.Parameters), resource.TestCheckResourceAttr("cyral_rego_policy_instance.policy_1", - regoPolicyInstanceEnabledKey, fmt.Sprintf("%t", parameters.policy.RegoPolicyInstance.Enabled)), + regopolicy.RegoPolicyInstanceEnabledKey, fmt.Sprintf("%t", parameters.policy.RegoPolicyInstance.Enabled)), resource.TestCheckResourceAttr("cyral_rego_policy_instance.policy_1", - fmt.Sprintf("%s.#", regoPolicyInstanceTagsKey), + fmt.Sprintf("%s.#", regopolicy.RegoPolicyInstanceTagsKey), fmt.Sprintf("%d", len(parameters.policy.RegoPolicyInstance.Tags))), resource.TestCheckResourceAttr("cyral_rego_policy_instance.policy_1", - fmt.Sprintf("%s.#", regoPolicyInstanceCreatedKey), "1"), + fmt.Sprintf("%s.#", regopolicy.RegoPolicyInstanceCreatedKey), "1"), } var durationTestCheckFunc resource.TestCheckFunc if parameters.policy.Duration != "" { durationTestCheckFunc = resource.TestCheckResourceAttr("cyral_rego_policy_instance.policy_1", - regoPolicyInstanceDurationKey, parameters.policy.Duration) + regopolicy.RegoPolicyInstanceDurationKey, parameters.policy.Duration) } else { durationTestCheckFunc = resource.TestCheckNoResourceAttr("cyral_rego_policy_instance.policy_1", - regoPolicyInstanceDurationKey) + regopolicy.RegoPolicyInstanceDurationKey) } testCheckFuncs = append(testCheckFuncs, durationTestCheckFunc) var lastUpdatedTestCheckFunc resource.TestCheckFunc if parameters.isUpdateOperation { lastUpdatedTestCheckFunc = resource.TestCheckResourceAttr("cyral_rego_policy_instance.policy_1", - fmt.Sprintf("%s.#", regoPolicyInstanceLastUpdatedKey), "1") + fmt.Sprintf("%s.#", regopolicy.RegoPolicyInstanceLastUpdatedKey), "1") } else { lastUpdatedTestCheckFunc = resource.TestCheckResourceAttr("cyral_rego_policy_instance.policy_1", - fmt.Sprintf("%s.#", regoPolicyInstanceLastUpdatedKey), "0") + fmt.Sprintf("%s.#", regopolicy.RegoPolicyInstanceLastUpdatedKey), "0") } testCheckFuncs = append(testCheckFuncs, lastUpdatedTestCheckFunc) @@ -200,13 +203,13 @@ func testAccRegoPolicyInstanceCheck( repoIDs := parameters.policy.RegoPolicyInstance.Scope.RepoIDs scopeTestCheckFunc = resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("cyral_rego_policy_instance.policy_1", - fmt.Sprintf("%s.0.%s.#", regoPolicyInstanceScopeKey, regoPolicyInstanceRepoIDsKey), + fmt.Sprintf("%s.0.%s.#", regopolicy.RegoPolicyInstanceScopeKey, regopolicy.RegoPolicyInstanceRepoIDsKey), fmt.Sprintf("%d", len(repoIDs)), ), ) } else { scopeTestCheckFunc = resource.TestCheckResourceAttr("cyral_rego_policy_instance.policy_1", - fmt.Sprintf("%s.#", regoPolicyInstanceScopeKey), "0") + fmt.Sprintf("%s.#", regopolicy.RegoPolicyInstanceScopeKey), "0") } testCheckFuncs = append(testCheckFuncs, scopeTestCheckFunc) diff --git a/cyral/resource_cyral_repository_access_gateway.go b/cyral/internal/repository/accessgateway/resource_cyral_repository_access_gateway.go similarity index 60% rename from cyral/resource_cyral_repository_access_gateway.go rename to cyral/internal/repository/accessgateway/resource_cyral_repository_access_gateway.go index 511aff77..4448d1dc 100644 --- a/cyral/resource_cyral_repository_access_gateway.go +++ b/cyral/internal/repository/accessgateway/resource_cyral_repository_access_gateway.go @@ -1,120 +1,122 @@ -package cyral +package accessgateway import ( "context" "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -type AccessGateway struct { +type AGData struct { SidecarId string `json:"sidecarId,omitempty"` BindingId string `json:"bindingId,omitempty"` } -type GetOrUpdateAccessGateway struct { - AccessGateway *AccessGateway `json:"accessGateway,omitempty"` +type AccessGateway struct { + AGData *AGData `json:"accessGateway,omitempty"` } -func (r *GetOrUpdateAccessGateway) WriteToSchema(d *schema.ResourceData) error { - d.SetId(d.Get(RepositoryIDKey).(string)) - d.Set(SidecarIDKey, r.AccessGateway.SidecarId) - d.Set(BindingIDKey, r.AccessGateway.BindingId) +func (r *AccessGateway) WriteToSchema(d *schema.ResourceData) error { + d.SetId(d.Get(utils.RepositoryIDKey).(string)) + d.Set(utils.SidecarIDKey, r.AGData.SidecarId) + d.Set(utils.BindingIDKey, r.AGData.BindingId) return nil } -func (r *GetOrUpdateAccessGateway) ReadFromSchema(d *schema.ResourceData) error { - r.AccessGateway = &AccessGateway{ - BindingId: d.Get(BindingIDKey).(string), - SidecarId: d.Get(SidecarIDKey).(string), +func (r *AccessGateway) ReadFromSchema(d *schema.ResourceData) error { + r.AGData = &AGData{ + BindingId: d.Get(utils.BindingIDKey).(string), + SidecarId: d.Get(utils.SidecarIDKey).(string), } return nil } -var ReadRepositoryAccessGatewayConfig = ResourceOperationConfig{ +var ReadRepositoryAccessGatewayConfig = core.ResourceOperationConfig{ Name: "RepositoryAccessGatewayRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf( "https://%s/v1/repos/%s/accessGateway", c.ControlPlane, - d.Get(RepositoryIDKey).(string), + d.Get(utils.RepositoryIDKey).(string), ) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { - return &GetOrUpdateAccessGateway{} + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { + return &AccessGateway{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "Repository access gateway"}, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Repository access gateway"}, } -func resourceRepositoryAccessGateway() *schema.Resource { +func ResourceRepositoryAccessGateway() *schema.Resource { return &schema.Resource{ Description: "Manages the sidecar and binding set as the access gateway for [cyral_repositories](./repositories.md).", - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "RepositoryAccessGatewayCreate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf( "https://%s/v1/repos/%s/accessGateway", c.ControlPlane, - d.Get(RepositoryIDKey).(string), + d.Get(utils.RepositoryIDKey).(string), ) }, - NewResourceData: func() ResourceData { - return &GetOrUpdateAccessGateway{} + NewResourceData: func() core.ResourceData { + return &AccessGateway{} }, }, ReadRepositoryAccessGatewayConfig, ), - ReadContext: ReadResource(ReadRepositoryAccessGatewayConfig), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadRepositoryAccessGatewayConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "RepositoryAccessGatewayUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf( "https://%s/v1/repos/%s/accessGateway", c.ControlPlane, - d.Get(RepositoryIDKey).(string), + d.Get(utils.RepositoryIDKey).(string), ) }, - NewResourceData: func() ResourceData { - return &GetOrUpdateAccessGateway{} + NewResourceData: func() core.ResourceData { + return &AccessGateway{} }, }, ReadRepositoryAccessGatewayConfig, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "RepositoryAccessGatewayDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf( "https://%s/v1/repos/%s/accessGateway", c.ControlPlane, - d.Get(RepositoryIDKey).(string), + d.Get(utils.RepositoryIDKey).(string), ) }, }, ), Schema: map[string]*schema.Schema{ - RepositoryIDKey: { + utils.RepositoryIDKey: { Description: "ID of the repository the access gateway is associated with. This is also the " + "import ID for this resource.", Type: schema.TypeString, ForceNew: true, Required: true, }, - SidecarIDKey: { + utils.SidecarIDKey: { Description: "ID of the sidecar that will be set as the access gateway for the given repository.", Type: schema.TypeString, Required: true, }, - BindingIDKey: { + utils.BindingIDKey: { Description: "ID of the binding that will be set as the access gateway for the given repository. " + "Note that modifications to this field will result in terraform replacing the given " + "access gateway resource, since the access gateway must be deleted before binding. ", @@ -129,7 +131,7 @@ func resourceRepositoryAccessGateway() *schema.Resource { d *schema.ResourceData, m interface{}, ) ([]*schema.ResourceData, error) { - d.Set(RepositoryIDKey, d.Id()) + d.Set(utils.RepositoryIDKey, d.Id()) return []*schema.ResourceData{d}, nil }, }, diff --git a/cyral/resource_cyral_repository_access_gateway_test.go b/cyral/internal/repository/accessgateway/resource_cyral_repository_access_gateway_test.go similarity index 80% rename from cyral/resource_cyral_repository_access_gateway_test.go rename to cyral/internal/repository/accessgateway/resource_cyral_repository_access_gateway_test.go index 7debe1a8..1d0baaa4 100644 --- a/cyral/resource_cyral_repository_access_gateway_test.go +++ b/cyral/internal/repository/accessgateway/resource_cyral_repository_access_gateway_test.go @@ -1,9 +1,11 @@ -package cyral +package accessgateway_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -31,30 +33,30 @@ func (ag *accessGatewayTestConfig) listenerID() string { } func accessGatewayConfig(ag accessGatewayTestConfig) string { - config := formatBasicRepositoryIntoConfig( - basicRepositoryResName, - accTestName(repoAccessGatewayResourceName, "repo"), + config := utils.FormatBasicRepositoryIntoConfig( + utils.BasicRepositoryResName, + utils.AccTestName(repoAccessGatewayResourceName, "repo"), "mongodb", "mongo.local", ag.listenerPort, ) - config += formatBasicSidecarIntoConfig( + config += utils.FormatBasicSidecarIntoConfig( ag.sidecarResName, - accTestName(repoAccessGatewayResourceName, ag.sidecarResName), + utils.AccTestName(repoAccessGatewayResourceName, ag.sidecarResName), "docker", "", ) - config += formatBasicSidecarListenerIntoConfig( + config += utils.FormatBasicSidecarListenerIntoConfig( ag.listenerResName, ag.sidecarID(), "mongodb", ag.listenerPort, ) - config += formatBasicRepositoryBindingIntoConfig( + config += utils.FormatBasicRepositoryBindingIntoConfig( ag.bindingResName, ag.sidecarID(), - basicRepositoryID, + utils.BasicRepositoryID, ag.listenerID(), ) return config @@ -91,7 +93,7 @@ func TestAccRepositoryAccessGatewayResource(t *testing.T) { } resource.ParallelTest( t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ intialTest, updateSidecarTest, @@ -114,15 +116,15 @@ func repoAccessGatewayCheck(resName, sidecarResName, bindingResName string) reso resFullName := fmt.Sprintf("cyral_repository_access_gateway.%s", resName) checkFuncs := []resource.TestCheckFunc{ resource.TestCheckResourceAttrPair( - resFullName, SidecarIDKey, + resFullName, utils.SidecarIDKey, fmt.Sprintf("cyral_sidecar.%s", sidecarResName), "id", ), resource.TestCheckResourceAttrPair( - resFullName, RepositoryIDKey, - fmt.Sprintf("cyral_repository.%s", basicRepositoryResName), "id", + resFullName, utils.RepositoryIDKey, + fmt.Sprintf("cyral_repository.%s", utils.BasicRepositoryResName), "id", ), resource.TestCheckResourceAttrPair( - resFullName, BindingIDKey, + resFullName, utils.BindingIDKey, fmt.Sprintf("cyral_repository_binding.%s", bindingResName), "binding_id", ), } @@ -136,6 +138,6 @@ func repoAccessGatewayConfig(resName, sidecarID, bindingID string) string { repository_id = %s sidecar_id = %s binding_id = %s - }`, resName, basicRepositoryID, sidecarID, bindingID, + }`, resName, utils.BasicRepositoryID, sidecarID, bindingID, ) } diff --git a/cyral/resource_cyral_repository_access_rules.go b/cyral/internal/repository/accessrules/resource_cyral_repository_access_rules.go similarity index 87% rename from cyral/resource_cyral_repository_access_rules.go rename to cyral/internal/repository/accessrules/resource_cyral_repository_access_rules.go index eff6af49..fe114e83 100644 --- a/cyral/resource_cyral_repository_access_rules.go +++ b/cyral/internal/repository/accessrules/resource_cyral_repository_access_rules.go @@ -1,11 +1,13 @@ -package cyral +package accessrules import ( "context" "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -37,7 +39,7 @@ type AccessRulesResponse struct { // read call returned and translates it into the Terraform schema. func (arr *AccessRulesResponse) WriteToSchema(d *schema.ResourceData) error { d.SetId( - marshalComposedID( + utils.MarshalComposedID( []string{ d.Get("repository_id").(string), d.Get("user_account_id").(string), @@ -126,7 +128,7 @@ func (arr *AccessRulesResource) ReadFromSchema(d *schema.ResourceData) error { return nil } -var ReadRepositoryAccessRulesConfig = ResourceOperationConfig{ +var ReadRepositoryAccessRulesConfig = core.ResourceOperationConfig{ Name: "RepositoryAccessRulesRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -136,17 +138,17 @@ var ReadRepositoryAccessRulesConfig = ResourceOperationConfig{ d.Get("user_account_id").(string), ) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &AccessRulesResponse{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "Repository access rule"}, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Repository access rule"}, } -func resourceRepositoryAccessRules() *schema.Resource { +func ResourceRepositoryAccessRules() *schema.Resource { return &schema.Resource{ Description: "Manage access rules", - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "RepositoryAccessRulesCreate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -158,18 +160,18 @@ func resourceRepositoryAccessRules() *schema.Resource { userAccountID, ) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return &AccessRulesResource{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &AccessRulesResponse{} }, }, ReadRepositoryAccessRulesConfig, ), - ReadContext: ReadResource(ReadRepositoryAccessRulesConfig), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadRepositoryAccessRulesConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "RepositoryAccessRulesUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -179,22 +181,22 @@ func resourceRepositoryAccessRules() *schema.Resource { d.Get("user_account_id").(string), ) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return &AccessRulesResource{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &AccessRulesResponse{} }, }, ReadRepositoryAccessRulesConfig, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "RepositoryAccessRulesDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { - idPieces, err := unmarshalComposedID(d.Id(), "/", 2) + idPieces, err := utils.UnMarshalComposedID(d.Id(), "/", 2) if err != nil { panic(fmt.Sprintf("Failed to unmarshal access rules ID: %v", err)) } @@ -247,7 +249,7 @@ func resourceRepositoryAccessRules() *schema.Resource { Schema: map[string]*schema.Schema{ "type": { Description: "Identity type. List of supported values: " + - supportedTypesMarkdown([]string{ + utils.SupportedValuesAsMarkdown([]string{ "username", "email", "group", @@ -312,7 +314,7 @@ func resourceRepositoryAccessRules() *schema.Resource { d *schema.ResourceData, m interface{}, ) ([]*schema.ResourceData, error) { - ids, err := unmarshalComposedID(d.Id(), "/", 2) + ids, err := utils.UnMarshalComposedID(d.Id(), "/", 2) if err != nil { return nil, fmt.Errorf( "failed to unmarshal ID: %v", diff --git a/cyral/resource_cyral_repository_access_rules_test.go b/cyral/internal/repository/accessrules/resource_cyral_repository_access_rules_test.go similarity index 85% rename from cyral/resource_cyral_repository_access_rules_test.go rename to cyral/internal/repository/accessrules/resource_cyral_repository_access_rules_test.go index 42add329..0a0c4660 100644 --- a/cyral/resource_cyral_repository_access_rules_test.go +++ b/cyral/internal/repository/accessrules/resource_cyral_repository_access_rules_test.go @@ -1,9 +1,12 @@ -package cyral +package accessrules_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/accessrules" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -16,16 +19,16 @@ var validUntil string = "3022-01-02T10:20:30Z" var validFromUpdated string = "2023-11-12T10:20:30Z" var validUntilUpdated string = "3023-11-12T10:20:30Z" -var initialAccessRulesConfig *AccessRulesResource = &AccessRulesResource{ - AccessRules: []*AccessRule{ +var initialAccessRulesConfig *accessrules.AccessRulesResource = &accessrules.AccessRulesResource{ + AccessRules: []*accessrules.AccessRule{ { - Identity: &AccessRulesIdentity{ + Identity: &accessrules.AccessRulesIdentity{ Name: "identityEmail", Type: "email", }, ValidFrom: &validFrom, ValidUntil: &validUntil, - Config: &AccessRulesConfig{ + Config: &accessrules.AccessRulesConfig{ AuthorizationPolicyInstanceIDs: []string{ "policy1", "policy2", @@ -33,13 +36,13 @@ var initialAccessRulesConfig *AccessRulesResource = &AccessRulesResource{ }, }, { - Identity: &AccessRulesIdentity{ + Identity: &accessrules.AccessRulesIdentity{ Name: "identityGroup", Type: "group", }, ValidFrom: &validFrom, ValidUntil: &validUntil, - Config: &AccessRulesConfig{ + Config: &accessrules.AccessRulesConfig{ AuthorizationPolicyInstanceIDs: []string{ "policy3", "policy4", @@ -50,16 +53,16 @@ var initialAccessRulesConfig *AccessRulesResource = &AccessRulesResource{ } // Let's modify the identity names, the durations, and the policy IDs -var updatedAccessRulesConfig *AccessRulesResource = &AccessRulesResource{ - AccessRules: []*AccessRule{ +var updatedAccessRulesConfig *accessrules.AccessRulesResource = &accessrules.AccessRulesResource{ + AccessRules: []*accessrules.AccessRule{ { - Identity: &AccessRulesIdentity{ + Identity: &accessrules.AccessRulesIdentity{ Name: "identityEmailUpdated", Type: "email", }, ValidFrom: &validFromUpdated, ValidUntil: &validUntilUpdated, - Config: &AccessRulesConfig{ + Config: &accessrules.AccessRulesConfig{ AuthorizationPolicyInstanceIDs: []string{ "policy1Updated", "policy2Updated", @@ -67,13 +70,13 @@ var updatedAccessRulesConfig *AccessRulesResource = &AccessRulesResource{ }, }, { - Identity: &AccessRulesIdentity{ + Identity: &accessrules.AccessRulesIdentity{ Name: "identityGroupUpdated", Type: "group", }, ValidFrom: &validFromUpdated, ValidUntil: &validUntilUpdated, - Config: &AccessRulesConfig{ + Config: &accessrules.AccessRulesConfig{ AuthorizationPolicyInstanceIDs: []string{ "policy3Updated", "policy4Updated", @@ -83,10 +86,10 @@ var updatedAccessRulesConfig *AccessRulesResource = &AccessRulesResource{ }, } -var barebonesAccessRulesConfig *AccessRulesResource = &AccessRulesResource{ - AccessRules: []*AccessRule{ +var barebonesAccessRulesConfig *accessrules.AccessRulesResource = &accessrules.AccessRulesResource{ + AccessRules: []*accessrules.AccessRule{ { - Identity: &AccessRulesIdentity{ + Identity: &accessrules.AccessRulesIdentity{ Name: "identityUsername", Type: "username", }, @@ -115,7 +118,7 @@ func TestAccRepositoryAccessRulesResource(t *testing.T) { resource.ParallelTest( t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -140,26 +143,26 @@ func TestAccRepositoryAccessRulesResource(t *testing.T) { } func setupRepositoryAccessRulesTest( - accessRulesData *AccessRulesResource, + accessRulesData *accessrules.AccessRulesResource, bareBones bool, ) (string, resource.TestCheckFunc) { var configuration string - configuration += formatBasicRepositoryIntoConfig( - basicRepositoryResName, - accTestName(repositoryAccessRulesResourceName, "repository1"), + configuration += utils.FormatBasicRepositoryIntoConfig( + utils.BasicRepositoryResName, + utils.AccTestName(repositoryAccessRulesResourceName, "repository1"), "mongodb", "mongo.local", 3333, ) + "\n" configuration += userAccConfig( - basicRepositoryID, - accTestName(repositoryAccessRulesResourceName, "user_acount"), + utils.BasicRepositoryID, + utils.AccTestName(repositoryAccessRulesResourceName, "user_acount"), ) + "\n" configuration += accessRulesToConfig( accessRulesData, - basicRepositoryID, + utils.BasicRepositoryID, "cyral_repository_user_account.my_test_user_account.user_account_id", bareBones, ) + "\n" @@ -326,7 +329,7 @@ func userAccConfig( } func accessRulesToConfig( - res *AccessRulesResource, + res *accessrules.AccessRulesResource, repoID string, userAccountID string, bareBones bool, diff --git a/cyral/resource_cyral_repository_binding.go b/cyral/internal/repository/binding/resource_cyral_repository_binding.go similarity index 73% rename from cyral/resource_cyral_repository_binding.go rename to cyral/internal/repository/binding/resource_cyral_repository_binding.go index d2ea3c66..c553cec5 100644 --- a/cyral/resource_cyral_repository_binding.go +++ b/cyral/internal/repository/binding/resource_cyral_repository_binding.go @@ -1,11 +1,13 @@ -package cyral +package binding import ( "context" "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -41,10 +43,10 @@ type GetBindingResponse struct { } func (r *CreateBindingResponse) WriteToSchema(d *schema.ResourceData) error { - d.Set(BindingIDKey, r.BindingID) - d.SetId(marshalComposedID( + d.Set(utils.BindingIDKey, r.BindingID) + d.SetId(utils.MarshalComposedID( []string{ - d.Get(SidecarIDKey).(string), + d.Get(utils.SidecarIDKey).(string), r.BindingID, }, "/")) return nil @@ -55,23 +57,23 @@ func (r *GetBindingResponse) WriteToSchema(d *schema.ResourceData) error { } func (r *Binding) WriteToSchema(d *schema.ResourceData) error { - d.Set(BindingIDKey, r.BindingID) + d.Set(utils.BindingIDKey, r.BindingID) d.Set(BindingEnabledKey, r.Enabled) - d.Set(RepositoryIDKey, r.RepoId) + d.Set(utils.RepositoryIDKey, r.RepoId) d.Set(ListenerBindingKey, r.ListenerBindingsAsInterface()) return nil } func (r *CreateBindingRequest) ReadFromSchema(d *schema.ResourceData) error { - r.SidecarID = d.Get(SidecarIDKey).(string) + r.SidecarID = d.Get(utils.SidecarIDKey).(string) r.Binding = &Binding{} return r.Binding.ReadFromSchema(d) } func (r *Binding) ReadFromSchema(d *schema.ResourceData) error { - r.BindingID = d.Get(BindingIDKey).(string) + r.BindingID = d.Get(utils.BindingIDKey).(string) r.Enabled = d.Get(BindingEnabledKey).(bool) - r.RepoId = d.Get(RepositoryIDKey).(string) + r.RepoId = d.Get(utils.RepositoryIDKey).(string) r.ListenerBindingsFromInterface(d.Get(ListenerBindingKey).([]interface{})) return nil } @@ -83,8 +85,8 @@ func (r *Binding) ListenerBindingsAsInterface() []interface{} { listenerBindings := make([]interface{}, len(r.ListenerBindings)) for i, listenerBinding := range r.ListenerBindings { listenerBindings[i] = map[string]interface{}{ - ListenerIDKey: listenerBinding.ListenerID, - NodeIndexKey: listenerBinding.NodeIndex, + utils.ListenerIDKey: listenerBinding.ListenerID, + NodeIndexKey: listenerBinding.NodeIndex, } } return listenerBindings @@ -97,77 +99,77 @@ func (r *Binding) ListenerBindingsFromInterface(i []interface{}) { listenerBindings := make([]*ListenerBinding, len(i)) for index, listenerBinding := range i { listenerBindings[index] = &ListenerBinding{ - ListenerID: listenerBinding.(map[string]interface{})[ListenerIDKey].(string), + ListenerID: listenerBinding.(map[string]interface{})[utils.ListenerIDKey].(string), NodeIndex: uint32(listenerBinding.(map[string]interface{})[NodeIndexKey].(int)), } } r.ListenerBindings = listenerBindings } -var ReadRepositoryBindingConfig = ResourceOperationConfig{ +var ReadRepositoryBindingConfig = core.ResourceOperationConfig{ Name: "RepositoryBindingResourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/sidecars/%s/bindings/%s", c.ControlPlane, - d.Get(SidecarIDKey).(string), - d.Get(BindingIDKey).(string), + d.Get(utils.SidecarIDKey).(string), + d.Get(utils.BindingIDKey).(string), ) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &GetBindingResponse{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "Repository binding"}, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Repository binding"}, } -func resourceRepositoryBinding() *schema.Resource { +func ResourceRepositoryBinding() *schema.Resource { return &schema.Resource{ Description: "Manages [cyral repository to sidecar bindings](https://cyral.com/docs/sidecars/sidecar-assign-repo).", - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "RepositoryBindingResourceCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/sidecars/%s/bindings", c.ControlPlane, - d.Get(SidecarIDKey).(string)) + d.Get(utils.SidecarIDKey).(string)) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return &CreateBindingRequest{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &CreateBindingResponse{} }, }, ReadRepositoryBindingConfig, ), - ReadContext: ReadResource(ReadRepositoryBindingConfig), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadRepositoryBindingConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "RepositoryBindingResourceUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/sidecars/%s/bindings/%s", c.ControlPlane, - d.Get(SidecarIDKey).(string), - d.Get(BindingIDKey).(string), + d.Get(utils.SidecarIDKey).(string), + d.Get(utils.BindingIDKey).(string), ) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return &CreateBindingRequest{} }, }, ReadRepositoryBindingConfig, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "RepositoryBindingResourceDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/sidecars/%s/bindings/%s", c.ControlPlane, - d.Get(SidecarIDKey).(string), - d.Get(BindingIDKey).(string), + d.Get(utils.SidecarIDKey).(string), + d.Get(utils.BindingIDKey).(string), ) }, }, @@ -175,18 +177,18 @@ func resourceRepositoryBinding() *schema.Resource { SchemaVersion: 2, Schema: map[string]*schema.Schema{ - BindingIDKey: { + utils.BindingIDKey: { Description: "ID of the binding. Computed and assigned to binding at the time of creation.", Computed: true, Type: schema.TypeString, }, - SidecarIDKey: { + utils.SidecarIDKey: { Description: "ID of the sidecar that will be bound to the given repository.", Required: true, ForceNew: true, Type: schema.TypeString, }, - RepositoryIDKey: { + utils.RepositoryIDKey: { Description: "ID of the repository that will be bound to the sidecar.", Required: true, ForceNew: true, @@ -204,7 +206,7 @@ func resourceRepositoryBinding() *schema.Resource { Required: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - ListenerIDKey: { + utils.ListenerIDKey: { Description: "The sidecar listener that this binding is associated with.", Required: true, Type: schema.TypeString, @@ -225,12 +227,12 @@ func resourceRepositoryBinding() *schema.Resource { d *schema.ResourceData, m interface{}, ) ([]*schema.ResourceData, error) { - ids, err := unmarshalComposedID(d.Id(), "/", 2) + ids, err := utils.UnMarshalComposedID(d.Id(), "/", 2) if err != nil { return nil, err } - d.Set(SidecarIDKey, ids[0]) - d.Set(BindingIDKey, ids[1]) + d.Set(utils.SidecarIDKey, ids[0]) + d.Set(utils.BindingIDKey, ids[1]) return []*schema.ResourceData{d}, nil }, }, diff --git a/cyral/resource_cyral_repository_binding_test.go b/cyral/internal/repository/binding/resource_cyral_repository_binding_test.go similarity index 55% rename from cyral/resource_cyral_repository_binding_test.go rename to cyral/internal/repository/binding/resource_cyral_repository_binding_test.go index f1fcbe76..d76582f0 100644 --- a/cyral/resource_cyral_repository_binding_test.go +++ b/cyral/internal/repository/binding/resource_cyral_repository_binding_test.go @@ -1,10 +1,13 @@ -package cyral +package binding_test import ( "fmt" "strconv" "testing" + bind "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/binding" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -13,18 +16,18 @@ const ( repoBindingRepoName = "repo-for-bindings-test" ) -var initialConfig = Binding{ +var initialConfig = bind.Binding{ Enabled: true, - ListenerBindings: []*ListenerBinding{ + ListenerBindings: []*bind.ListenerBinding{ { NodeIndex: 0, }, }, } -var updatedConfig = Binding{ +var updatedConfig = bind.Binding{ Enabled: false, - ListenerBindings: []*ListenerBinding{ + ListenerBindings: []*bind.ListenerBinding{ { NodeIndex: 0, }, @@ -32,22 +35,22 @@ var updatedConfig = Binding{ } func bindingRepoSidecarListenerConfig() string { - config := formatBasicRepositoryIntoConfig( - basicRepositoryResName, - accTestName(repoBindingRepoName, "repo"), + config := utils.FormatBasicRepositoryIntoConfig( + utils.BasicRepositoryResName, + utils.AccTestName(repoBindingRepoName, "repo"), "mongodb", "mongo.local", 27017, ) - config += formatBasicSidecarIntoConfig( - basicSidecarResName, - accTestName(repoBindingSidecarName, "sidecar"), + config += utils.FormatBasicSidecarIntoConfig( + utils.BasicSidecarResName, + utils.AccTestName(repoBindingSidecarName, "sidecar"), "docker", "", ) - config += formatBasicSidecarListenerIntoConfig( - basicListenerResName, - basicSidecarID, + config += utils.FormatBasicSidecarListenerIntoConfig( + utils.BasicListenerResName, + utils.BasicSidecarID, "mongodb", 27017, ) @@ -65,7 +68,7 @@ func TestAccRepositoryBindingResource(t *testing.T) { } resource.ParallelTest( t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ intialTest, updateTest, @@ -75,7 +78,7 @@ func TestAccRepositoryBindingResource(t *testing.T) { ) } -func repositoryBindingTestStep(resName string, binding Binding) resource.TestStep { +func repositoryBindingTestStep(resName string, binding bind.Binding) resource.TestStep { config := bindingRepoSidecarListenerConfig() + repoBindingConfig(resName, binding) return resource.TestStep{ @@ -84,20 +87,20 @@ func repositoryBindingTestStep(resName string, binding Binding) resource.TestSte } } -func repoBindingCheck(resName string, binding Binding) resource.TestCheckFunc { +func repoBindingCheck(resName string, binding bind.Binding) resource.TestCheckFunc { resFullName := fmt.Sprintf("cyral_repository_binding.%s", resName) checkFuncs := []resource.TestCheckFunc{ resource.TestCheckResourceAttrPair( - resFullName, SidecarIDKey, - fmt.Sprintf("cyral_sidecar.%s", basicSidecarResName), "id", + resFullName, utils.SidecarIDKey, + fmt.Sprintf("cyral_sidecar.%s", utils.BasicSidecarResName), "id", ), resource.TestCheckResourceAttrPair( - resFullName, RepositoryIDKey, - fmt.Sprintf("cyral_repository.%s", basicRepositoryResName), "id", + resFullName, utils.RepositoryIDKey, + fmt.Sprintf("cyral_repository.%s", utils.BasicRepositoryResName), "id", ), resource.TestCheckResourceAttr( resFullName, - BindingEnabledKey, strconv.FormatBool(binding.Enabled), + bind.BindingEnabledKey, strconv.FormatBool(binding.Enabled), ), } @@ -105,13 +108,13 @@ func repoBindingCheck(resName string, binding Binding) resource.TestCheckFunc { checkFuncs = append( checkFuncs, []resource.TestCheckFunc{ resource.TestCheckResourceAttrPair( - resFullName, fmt.Sprintf("%s.%d.%s", ListenerBindingKey, i, ListenerIDKey), - fmt.Sprintf("cyral_sidecar_listener.%s", basicListenerResName), - ListenerIDKey, + resFullName, fmt.Sprintf("%s.%d.%s", bind.ListenerBindingKey, i, utils.ListenerIDKey), + fmt.Sprintf("cyral_sidecar_listener.%s", utils.BasicListenerResName), + utils.ListenerIDKey, ), resource.TestCheckResourceAttr( resFullName, - fmt.Sprintf("%s.%d.%s", ListenerBindingKey, i, NodeIndexKey), + fmt.Sprintf("%s.%d.%s", bind.ListenerBindingKey, i, bind.NodeIndexKey), strconv.Itoa(int(binding.NodeIndex)), ), }..., @@ -120,14 +123,14 @@ func repoBindingCheck(resName string, binding Binding) resource.TestCheckFunc { return resource.ComposeTestCheckFunc(checkFuncs...) } -func repoBindingConfig(resName string, binding Binding) string { +func repoBindingConfig(resName string, binding bind.Binding) string { config := fmt.Sprintf( ` resource "cyral_repository_binding" "%s" { sidecar_id = %s repository_id = %s enabled = %s`, - resName, basicSidecarID, basicRepositoryID, + resName, utils.BasicSidecarID, utils.BasicRepositoryID, strconv.FormatBool(binding.Enabled), ) @@ -137,7 +140,7 @@ func repoBindingConfig(resName string, binding Binding) string { listener_binding { listener_id = %s node_index = %d - }`, basicListenerID, binding.NodeIndex, + }`, utils.BasicListenerID, binding.NodeIndex, ) } config += ` diff --git a/cyral/resource_cyral_repository_conf_analysis.go b/cyral/internal/repository/confanalysis/resource_cyral_repository_conf_analysis.go similarity index 92% rename from cyral/resource_cyral_repository_conf_analysis.go rename to cyral/internal/repository/confanalysis/resource_cyral_repository_conf_analysis.go index 95825316..fe10deb9 100644 --- a/cyral/resource_cyral_repository_conf_analysis.go +++ b/cyral/internal/repository/confanalysis/resource_cyral_repository_conf_analysis.go @@ -1,4 +1,4 @@ -package cyral +package confanalysis import ( "context" @@ -7,7 +7,8 @@ import ( "log" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -130,7 +131,7 @@ func repositoryConfAnalysisResourceSchemaV0() *schema.Resource { // Previously, the ID for cyral_repository_conf_analysis had the format // {repository_id}/ConfAnalysis. The goal of this state upgrade is to remove // this suffix `ConfAnalysis`. -func upgradeRepositoryConfAnalysisV0( +func UpgradeRepositoryConfAnalysisV0( _ context.Context, rawState map[string]interface{}, _ interface{}, @@ -139,7 +140,7 @@ func upgradeRepositoryConfAnalysisV0( return rawState, nil } -func resourceRepositoryConfAnalysis() *schema.Resource { +func ResourceRepositoryConfAnalysis() *schema.Resource { return &schema.Resource{ Description: "Manages Repository Analysis Configuration. This resource allows configuring both " + "[Log Settings](https://cyral.com/docs/manage-repositories/repo-log-volume) " + @@ -156,7 +157,7 @@ func resourceRepositoryConfAnalysis() *schema.Resource { Version: 0, Type: repositoryConfAnalysisResourceSchemaV0(). CoreConfigSchema().ImpliedType(), - Upgrade: upgradeRepositoryConfAnalysisV0, + Upgrade: UpgradeRepositoryConfAnalysisV0, }, }, @@ -181,19 +182,19 @@ func resourceRepositoryConfAnalysisCreate(ctx context.Context, d *schema.Resourc resourceData, err := getConfAnalysisDataFromResource(d) if err != nil { - return createError("Unable to create conf analysis", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to create conf analysis", fmt.Sprintf("%v", err)) } url := fmt.Sprintf("https://%s/v1/repos/%s/conf/analysis", c.ControlPlane, d.Get("repository_id")) body, err := c.DoRequest(url, http.MethodPut, resourceData.Config) if err != nil { - return createError("Unable to create conf analysis", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to create conf analysis", fmt.Sprintf("%v", err)) } response := RepositoryConfAnalysisData{} if err := json.Unmarshal(body, &response); err != nil { - return createError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] Response body (unmarshalled): %#v", response) @@ -213,13 +214,13 @@ func resourceRepositoryConfAnalysisRead(ctx context.Context, d *schema.ResourceD body, err := c.DoRequest(url, http.MethodGet, nil) if err != nil { - return createError(fmt.Sprintf("Unable to read conf analysis. Conf Analysis Id: %s", + return utils.CreateError(fmt.Sprintf("Unable to read conf analysis. Conf Analysis Id: %s", d.Id()), fmt.Sprintf("%v", err)) } response := RepositoryConfAnalysisData{} if err := json.Unmarshal(body, &response); err != nil { - return createError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] Response body (unmarshalled): %#v", response) @@ -237,13 +238,13 @@ func resourceRepositoryConfAnalysisUpdate(ctx context.Context, d *schema.Resourc resourceData, err := getConfAnalysisDataFromResource(d) if err != nil { - return createError("Unable to update conf analysis", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to update conf analysis", fmt.Sprintf("%v", err)) } url := fmt.Sprintf("https://%s/v1/repos/%s/conf/analysis", c.ControlPlane, d.Get("repository_id")) if _, err := c.DoRequest(url, http.MethodPut, resourceData.Config); err != nil { - return createError("Unable to update conf analysis", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to update conf analysis", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] End resourceRepositoryConfAnalysisUpdate") @@ -258,7 +259,7 @@ func resourceRepositoryConfAnalysisDelete(ctx context.Context, d *schema.Resourc url := fmt.Sprintf("https://%s/v1/repos/%s/conf/analysis", c.ControlPlane, d.Get("repository_id")) if _, err := c.DoRequest(url, http.MethodDelete, nil); err != nil { - return createError("Unable to delete conf analysis", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to delete conf analysis", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] End resourceRepositoryConfAnalysisDelete") diff --git a/cyral/resource_cyral_repository_conf_analysis_test.go b/cyral/internal/repository/confanalysis/resource_cyral_repository_conf_analysis_test.go similarity index 90% rename from cyral/resource_cyral_repository_conf_analysis_test.go rename to cyral/internal/repository/confanalysis/resource_cyral_repository_conf_analysis_test.go index fae3019c..c1e4a668 100644 --- a/cyral/resource_cyral_repository_conf_analysis_test.go +++ b/cyral/internal/repository/confanalysis/resource_cyral_repository_conf_analysis_test.go @@ -1,4 +1,4 @@ -package cyral +package confanalysis_test import ( "context" @@ -6,6 +6,9 @@ import ( "regexp" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/confanalysis" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/stretchr/testify/require" ) @@ -15,9 +18,9 @@ const ( ) func repositoryConfAnalysisSampleRepositoryConfig() string { - return formatBasicRepositoryIntoConfig( - basicRepositoryResName, - accTestName(repositoryConfAnalysisResourceName, "repository"), + return utils.FormatBasicRepositoryIntoConfig( + utils.BasicRepositoryResName, + utils.AccTestName(repositoryConfAnalysisResourceName, "repository"), "postgresql", "some-hostname", 3067, @@ -26,7 +29,7 @@ func repositoryConfAnalysisSampleRepositoryConfig() string { func TestAccRepositoryConfAnalysisResource(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testAccRepoConfAnalysisConfig_ErrorRedact(), @@ -62,7 +65,7 @@ func TestRepositoryConfAnalysisResourceUpgradeV0(t *testing.T) { "id": "repositoryID/ConfAnalysis", "repository_id": "repositoryID", } - actualNewState, err := upgradeRepositoryConfAnalysisV0(context.Background(), + actualNewState, err := confanalysis.UpgradeRepositoryConfAnalysisV0(context.Background(), previousState, nil) require.NoError(t, err) expectedNewState := map[string]interface{}{ @@ -79,7 +82,7 @@ func testAccRepoConfAnalysisConfig_ErrorRedact() string { resource "cyral_repository_conf_analysis" "test_conf_analysis" { repository_id = %s redact = "some-invalid-value" - }`, basicRepositoryID) + }`, utils.BasicRepositoryID) return config } @@ -92,7 +95,7 @@ func testAccRepoConfAnalysisConfig_ErrorAnnotationGroups() string { comment_annotation_groups = [ "some-invalid-value" ] - }`, basicRepositoryID) + }`, utils.BasicRepositoryID) return config } @@ -105,7 +108,7 @@ func testAccRepoConfAnalysisConfig_ErrorLogGroups() string { log_groups = [ "some-invalid-value" ] - }`, basicRepositoryID) + }`, utils.BasicRepositoryID) return config } @@ -115,7 +118,7 @@ func testAccRepoConfAnalysisConfig_DefaultValues() string { config += fmt.Sprintf(` resource "cyral_repository_conf_analysis" "test_conf_analysis" { repository_id = %s - }`, basicRepositoryID) + }`, utils.BasicRepositoryID) return config } @@ -164,7 +167,7 @@ func testAccRepoConfAnalysisConfig_Updated() string { "sensitive & dml", "sensitive & ddl" ] - }`, basicRepositoryID) + }`, utils.BasicRepositoryID) return config } diff --git a/cyral/resource_cyral_repository_conf_auth.go b/cyral/internal/repository/confauth/resource_cyral_repository_conf_auth.go similarity index 77% rename from cyral/resource_cyral_repository_conf_auth.go rename to cyral/internal/repository/confauth/resource_cyral_repository_conf_auth.go index 68c68fd5..fafad39d 100644 --- a/cyral/resource_cyral_repository_conf_auth.go +++ b/cyral/internal/repository/confauth/resource_cyral_repository_conf_auth.go @@ -1,4 +1,4 @@ -package cyral +package confauth import ( "context" @@ -7,7 +7,10 @@ import ( "log" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -16,17 +19,16 @@ import ( const ( repositoryConfAuthURLFormat = "https://%s/v1/repos/%s/conf/auth" - defaultClientTLS = "disable" - defaultRepoTLS = "disable" - - accessTokenAuthType = "ACCESS_TOKEN" - awsIAMAuthType = "AWS_IAM" - defaultAuthType = accessTokenAuthType + DefaultClientTLS = "disable" + DefaultRepoTLS = "disable" + AccessTokenAuthType = "ACCESS_TOKEN" + AwsIAMAuthType = "AWS_IAM" + DefaultAuthType = AccessTokenAuthType ) var authTypes = []string{ - accessTokenAuthType, - awsIAMAuthType, + AccessTokenAuthType, + AwsIAMAuthType, } type RepositoryConfAuthData struct { @@ -121,7 +123,7 @@ func resourceRepositoryConfAuthCreate( httpMethod = http.MethodPut } defer log.Printf("[DEBUG] End resourceRepositoryConfAuthCreate") - return CreateResource(CreateConfAuthConfig(httpMethod), ReadConfAuthConfig())(ctx, d, m) + return core.CreateResource(CreateConfAuthConfig(httpMethod), ReadConfAuthConfig())(ctx, d, m) } func confAuthAlreadyExists(c *client.Client, repositoryID string) bool { @@ -138,43 +140,43 @@ func confAuthAlreadyExists(c *client.Client, repositoryID string) bool { return true } -func CreateConfAuthConfig(httpMethod string) ResourceOperationConfig { - return ResourceOperationConfig{ +func CreateConfAuthConfig(httpMethod string) core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "ConfAuthResourceCreate", HttpMethod: httpMethod, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf(repositoryConfAuthURLFormat, c.ControlPlane, d.Get("repository_id")) }, - NewResourceData: func() ResourceData { return &RepositoryConfAuthData{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &CreateRepositoryConfAuthResponse{} }, + NewResourceData: func() core.ResourceData { return &RepositoryConfAuthData{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &CreateRepositoryConfAuthResponse{} }, } } -func ReadConfAuthConfig() ResourceOperationConfig { - return ResourceOperationConfig{ +func ReadConfAuthConfig() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "ConfAuthResourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf(repositoryConfAuthURLFormat, c.ControlPlane, d.Get("repository_id")) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &ReadRepositoryConfAuthResponse{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "Repository conf auth"}, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &ReadRepositoryConfAuthResponse{} }, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Repository conf auth"}, } } -func UpdateConfAuthConfig() ResourceOperationConfig { - return ResourceOperationConfig{ +func UpdateConfAuthConfig() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "ConfAuthResourceUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf(repositoryConfAuthURLFormat, c.ControlPlane, d.Get("repository_id")) }, - NewResourceData: func() ResourceData { return &RepositoryConfAuthData{} }, + NewResourceData: func() core.ResourceData { return &RepositoryConfAuthData{} }, } } -func DeleteConfAuthConfig() ResourceOperationConfig { - return ResourceOperationConfig{ +func DeleteConfAuthConfig() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "ConfAuthResourceDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -202,10 +204,10 @@ func repositoryConfAuthResourceSchemaV0() *schema.Resource { Optional: true, }, "client_tls": { - Description: fmt.Sprintf("Is the repo Client using TLS? Default is %q.", defaultClientTLS), + Description: fmt.Sprintf("Is the repo Client using TLS? Default is %q.", DefaultClientTLS), Type: schema.TypeString, Optional: true, - Default: defaultClientTLS, + Default: DefaultClientTLS, }, "identity_provider": { Description: "The ID (Alias) of the identity provider integration.", @@ -213,18 +215,18 @@ func repositoryConfAuthResourceSchemaV0() *schema.Resource { Optional: true, }, "repo_tls": { - Description: fmt.Sprintf("Is TLS enabled for the repository? Default is %q.", defaultRepoTLS), + Description: fmt.Sprintf("Is TLS enabled for the repository? Default is %q.", DefaultRepoTLS), Type: schema.TypeString, Optional: true, - Default: defaultRepoTLS, + Default: DefaultRepoTLS, }, "auth_type": { Description: fmt.Sprintf("Authentication type for this repository. **Note**: `%s` is currently "+ "only supported by `%s` repo type. List of supported values: %s", - awsIAMAuthType, MongoDB, supportedTypesMarkdown(authTypes)), + AwsIAMAuthType, repository.MongoDB, utils.SupportedValuesAsMarkdown(authTypes)), Type: schema.TypeString, Optional: true, - Default: defaultAuthType, + Default: DefaultAuthType, ValidateFunc: validation.StringInSlice(authTypes, false), }, }, @@ -234,7 +236,7 @@ func repositoryConfAuthResourceSchemaV0() *schema.Resource { // Previously, the id of the resource `cyral_repository_conf_auth` was hardcoded // to `repo-conf`, which doesn't make sense. The goal here is to set it to be // the repository ID. -func upgradeRepositoryConfAuthV0( +func UpgradeRepositoryConfAuthV0( _ context.Context, rawState map[string]interface{}, _ interface{}, @@ -243,13 +245,13 @@ func upgradeRepositoryConfAuthV0( return rawState, nil } -func resourceRepositoryConfAuth() *schema.Resource { +func ResourceRepositoryConfAuth() *schema.Resource { return &schema.Resource{ Description: "Manages the [Repository Authentication settings](https://cyral.com/docs/manage-repositories/repo-advanced-settings/#authentication) that is shown in the Advanced tab.", CreateContext: resourceRepositoryConfAuthCreate, - ReadContext: ReadResource(ReadConfAuthConfig()), - UpdateContext: UpdateResource(UpdateConfAuthConfig(), ReadConfAuthConfig()), - DeleteContext: DeleteResource(DeleteConfAuthConfig()), + ReadContext: core.ReadResource(ReadConfAuthConfig()), + UpdateContext: core.UpdateResource(UpdateConfAuthConfig(), ReadConfAuthConfig()), + DeleteContext: core.DeleteResource(DeleteConfAuthConfig()), SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ @@ -257,7 +259,7 @@ func resourceRepositoryConfAuth() *schema.Resource { Version: 0, Type: repositoryConfAuthResourceSchemaV0(). CoreConfigSchema().ImpliedType(), - Upgrade: upgradeRepositoryConfAuthV0, + Upgrade: UpgradeRepositoryConfAuthV0, }, }, diff --git a/cyral/resource_cyral_repository_conf_auth_test.go b/cyral/internal/repository/confauth/resource_cyral_repository_conf_auth_test.go similarity index 75% rename from cyral/resource_cyral_repository_conf_auth_test.go rename to cyral/internal/repository/confauth/resource_cyral_repository_conf_auth_test.go index cd017638..6ad02099 100644 --- a/cyral/resource_cyral_repository_conf_auth_test.go +++ b/cyral/internal/repository/confauth/resource_cyral_repository_conf_auth_test.go @@ -1,10 +1,13 @@ -package cyral +package confauth_test import ( "context" "fmt" "testing" + auth "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/confauth" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/stretchr/testify/require" ) @@ -14,17 +17,17 @@ const ( ) func repositoryConfAuthDependencyConfig() string { - return formatBasicRepositoryIntoConfig( - basicRepositoryResName, - accTestName(repositoryConfAuthResourceName, "repository"), + return utils.FormatBasicRepositoryIntoConfig( + utils.BasicRepositoryResName, + utils.AccTestName(repositoryConfAuthResourceName, "repository"), "mongodb", "http://mongodb.local/", 27017, ) } -func initialRepositoryConfAuthConfig() RepositoryConfAuthData { - return RepositoryConfAuthData{ +func initialRepositoryConfAuthConfig() auth.RepositoryConfAuthData { + return auth.RepositoryConfAuthData{ AllowNativeAuth: false, ClientTLS: "disable", RepoTLS: "enable", @@ -32,8 +35,8 @@ func initialRepositoryConfAuthConfig() RepositoryConfAuthData { } } -func update1RepositoryConfAuthConfig() RepositoryConfAuthData { - return RepositoryConfAuthData{ +func update1RepositoryConfAuthConfig() auth.RepositoryConfAuthData { + return auth.RepositoryConfAuthData{ AllowNativeAuth: true, ClientTLS: "enable", RepoTLS: "disable", @@ -41,8 +44,8 @@ func update1RepositoryConfAuthConfig() RepositoryConfAuthData { } } -func update2RepositoryConfAuthConfig() RepositoryConfAuthData { - return RepositoryConfAuthData{ +func update2RepositoryConfAuthConfig() auth.RepositoryConfAuthData { + return auth.RepositoryConfAuthData{ AllowNativeAuth: false, ClientTLS: "enable", RepoTLS: "disable", @@ -59,16 +62,16 @@ func repositoryConfAuthMinimalConfigTest(resName string) resource.TestStep { config += fmt.Sprintf(` resource "cyral_repository_conf_auth" "%s" { repository_id = %s - }`, resName, basicRepositoryID) + }`, resName, utils.BasicRepositoryID) return resource.TestStep{ Config: config, Check: setupRepositoryConfAuthCheck( resName, - RepositoryConfAuthData{ - ClientTLS: defaultClientTLS, - RepoTLS: defaultRepoTLS, - AuthType: defaultAuthType, + auth.RepositoryConfAuthData{ + ClientTLS: auth.DefaultClientTLS, + RepoTLS: auth.DefaultRepoTLS, + AuthType: auth.DefaultAuthType, }, ), } @@ -81,7 +84,7 @@ func TestAccRepositoryConfAuthResource(t *testing.T) { mainTestUpdate2 := setupRepositoryConfAuthTest("main_test", update2RepositoryConfAuthConfig()) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ testMinimal, mainTest, @@ -101,7 +104,7 @@ func TestRepositoryConfAuthResourceUpgradeV0(t *testing.T) { "id": "repo-conf", "repository_id": "my-repository-id", } - actualNewState, err := upgradeRepositoryConfAuthV0(context.Background(), + actualNewState, err := auth.UpgradeRepositoryConfAuthV0(context.Background(), previousState, nil) require.NoError(t, err) expectedNewState := map[string]interface{}{ @@ -111,23 +114,23 @@ func TestRepositoryConfAuthResourceUpgradeV0(t *testing.T) { require.Equal(t, expectedNewState, actualNewState) } -func setupRepositoryConfAuthTest(resName string, repositoryConf RepositoryConfAuthData) resource.TestStep { +func setupRepositoryConfAuthTest(resName string, repositoryConf auth.RepositoryConfAuthData) resource.TestStep { return resource.TestStep{ Config: setupRepositoryConfAuthConfig(resName, repositoryConf), Check: setupRepositoryConfAuthCheck(resName, repositoryConf), } } -func setupRepositoryConfAuthConfig(resName string, repositoryConf RepositoryConfAuthData) string { +func setupRepositoryConfAuthConfig(resName string, repositoryConf auth.RepositoryConfAuthData) string { var config string config += repositoryConfAuthDependencyConfig() config += formatRepositoryConfAuthDataIntoConfig( - resName, repositoryConf, basicRepositoryID) + resName, repositoryConf, utils.BasicRepositoryID) return config } -func setupRepositoryConfAuthCheck(resName string, repositoryConf RepositoryConfAuthData) resource.TestCheckFunc { +func setupRepositoryConfAuthCheck(resName string, repositoryConf auth.RepositoryConfAuthData) resource.TestCheckFunc { resourceFullName := fmt.Sprintf("cyral_repository_conf_auth.%s", resName) return resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(resourceFullName, @@ -145,7 +148,7 @@ func setupRepositoryConfAuthCheck(resName string, repositoryConf RepositoryConfA func formatRepositoryConfAuthDataIntoConfig( resName string, - data RepositoryConfAuthData, + data auth.RepositoryConfAuthData, repositoryID string, ) string { return fmt.Sprintf(` diff --git a/cyral/data_source_cyral_repository.go b/cyral/internal/repository/data_source_cyral_repository.go similarity index 88% rename from cyral/data_source_cyral_repository.go rename to cyral/internal/repository/data_source_cyral_repository.go index 07b8abb7..c62ca661 100644 --- a/cyral/data_source_cyral_repository.go +++ b/cyral/internal/repository/data_source_cyral_repository.go @@ -1,4 +1,4 @@ -package cyral +package repository import ( "fmt" @@ -8,7 +8,9 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" ) const ( @@ -51,28 +53,28 @@ func (resp *GetReposResponse) WriteToSchema(d *schema.ResourceData) error { return nil } -func dataSourceRepositoryReadConfig() ResourceOperationConfig { - return ResourceOperationConfig{ +func dataSourceRepositoryReadConfig() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "RepositoryDataSourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { nameFilter := d.Get("name").(string) typeFilter := d.Get("type").(string) - urlParams := urlQuery(map[string]string{ + urlParams := utils.UrlQuery(map[string]string{ "name": nameFilter, "type": typeFilter, }) return fmt.Sprintf("https://%s/v1/repos%s", c.ControlPlane, urlParams) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &GetReposResponse{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &GetReposResponse{} }, } } -func dataSourceRepository() *schema.Resource { +func DataSourceRepository() *schema.Resource { return &schema.Resource{ Description: "Retrieves a list of repositories. See [`repository_list`](#nestedatt--repository_list).", - ReadContext: ReadResource(dataSourceRepositoryReadConfig()), + ReadContext: core.ReadResource(dataSourceRepositoryReadConfig()), Schema: map[string]*schema.Schema{ RepoNameKey: { Description: "Filter the results by a regular expression (regex) that matches names of existing repositories.", @@ -80,10 +82,10 @@ func dataSourceRepository() *schema.Resource { Optional: true, }, RepoTypeKey: { - Description: "Filter the results by type of repository. List of supported types:" + supportedTypesMarkdown(repositoryTypes()), + Description: "Filter the results by type of repository. List of supported types:" + utils.SupportedValuesAsMarkdown(RepositoryTypes()), Type: schema.TypeString, Optional: true, - ValidateFunc: validation.StringInSlice(append(repositoryTypes(), ""), false), + ValidateFunc: validation.StringInSlice(append(RepositoryTypes(), ""), false), }, RepoListKey: { Description: "List of existing repositories satisfying the filter criteria.", @@ -175,7 +177,7 @@ func dataSourceRepository() *schema.Resource { Computed: true, }, RepoMongoDBServerTypeKey: { - Description: "Type of the MongoDB server. Allowed values: " + supportedTypesMarkdown(mongoServerTypes()), + Description: "Type of the MongoDB server. Allowed values: " + utils.SupportedValuesAsMarkdown(mongoServerTypes()), Type: schema.TypeString, Computed: true, }, diff --git a/cyral/data_source_cyral_repository_test.go b/cyral/internal/repository/data_source_cyral_repository_test.go similarity index 77% rename from cyral/data_source_cyral_repository_test.go rename to cyral/internal/repository/data_source_cyral_repository_test.go index b4a279dc..9d11acc3 100644 --- a/cyral/data_source_cyral_repository_test.go +++ b/cyral/internal/repository/data_source_cyral_repository_test.go @@ -1,24 +1,28 @@ -package cyral +package repository_test import ( "fmt" "strconv" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const ( repositoryDataSourceName = "data-repository" ) -func repositoryDataSourceTestRepos() []RepoInfo { - return []RepoInfo{ +func repositoryDataSourceTestRepos() []repository.RepoInfo { + return []repository.RepoInfo{ { - Name: accTestName(repositoryDataSourceName, "sqlserver-1"), + Name: utils.AccTestName(repositoryDataSourceName, "sqlserver-1"), Type: "sqlserver", Labels: []string{"rds", "us-east-2"}, - RepoNodes: []*RepoNode{ + RepoNodes: []*repository.RepoNode{ { Host: "sql.local", Port: 3333, @@ -26,17 +30,17 @@ func repositoryDataSourceTestRepos() []RepoInfo { }, }, { - Name: accTestName(repositoryDataSourceName, "mongodb-1"), + Name: utils.AccTestName(repositoryDataSourceName, "mongodb-1"), Type: "mongodb", Labels: []string{"rds", "us-east-1"}, - RepoNodes: []*RepoNode{ + RepoNodes: []*repository.RepoNode{ { Host: "mongo.local", Port: 27017, }, }, - MongoDBSettings: &MongoDBSettings{ - ServerType: Standalone, + MongoDBSettings: &repository.MongoDBSettings{ + ServerType: repository.Standalone, }, }, } @@ -52,7 +56,11 @@ func TestAccRepositoryDataSource(t *testing.T) { repositoryDataSourceTestRepos(), fmt.Sprintf("^%s$", testRepos[1].Name), "mongodb") resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: testConfigNameFilter, @@ -70,14 +78,14 @@ func TestAccRepositoryDataSource(t *testing.T) { }) } -func testRepositoryDataSource(repoDatas []RepoInfo, nameFilter, typeFilter string) ( +func testRepositoryDataSource(repoDatas []repository.RepoInfo, nameFilter, typeFilter string) ( string, resource.TestCheckFunc, ) { return testRepositoryDataSourceConfig(repoDatas, nameFilter, typeFilter), testRepositoryDataSourceChecks(repoDatas, nameFilter, typeFilter) } -func testRepositoryDataSourceConfig(repoDatas []RepoInfo, nameFilter, typeFilter string) string { +func testRepositoryDataSourceConfig(repoDatas []repository.RepoInfo, nameFilter, typeFilter string) string { var config string var dependsOn []string for _, repoData := range repoDatas { @@ -89,16 +97,16 @@ func testRepositoryDataSourceConfig(repoDatas []RepoInfo, nameFilter, typeFilter return config } -func testRepositoryDataSourceChecks(repoDatas []RepoInfo, nameFilter, typeFilter string) resource.TestCheckFunc { +func testRepositoryDataSourceChecks(repoDatas []repository.RepoInfo, nameFilter, typeFilter string) resource.TestCheckFunc { dataSourceFullName := "data.cyral_repository.test_repository" if nameFilter == "" { return resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr(dataSourceFullName, "repository_list.#", - notZeroRegex(), + utils.NotZeroRegex(), ), - dsourceCheckTypeFilter( + utils.DSourceCheckTypeFilter( dataSourceFullName, "repository_list.%d.type", typeFilter, @@ -171,8 +179,8 @@ func testRepositoryDataSourceChecks(repoDatas []RepoInfo, nameFilter, typeFilter return testFunction } -func filterRepoDatas(repoDatas []RepoInfo, nameFilter, typeFilter string) []RepoInfo { - var filteredRepoDatas []RepoInfo +func filterRepoDatas(repoDatas []repository.RepoInfo, nameFilter, typeFilter string) []repository.RepoInfo { + var filteredRepoDatas []repository.RepoInfo for _, repoData := range repoDatas { if (nameFilter == "" || repoData.Name == nameFilter) && (typeFilter == "" || repoData.Type == typeFilter) { @@ -188,5 +196,5 @@ func repositoryDataSourceConfig(nameFilter, typeFilter string, dependsOn []strin depends_on = %s name = "%s" type = "%s" - }`, listToStr(dependsOn), nameFilter, typeFilter) + }`, utils.ListToStr(dependsOn), nameFilter, typeFilter) } diff --git a/cyral/internal/repository/datamap/model.go b/cyral/internal/repository/datamap/model.go new file mode 100644 index 00000000..a9b89892 --- /dev/null +++ b/cyral/internal/repository/datamap/model.go @@ -0,0 +1,106 @@ +package datamap + +import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +type DataMapRequest struct { + DataMap `json:"dataMap,omitempty"` +} + +// This is called 'DataMap' and not 'Datamap', because although we consider +// 'datamap' to be a single word in the resource name 'cyral_repository_datamap' +// for ease of writing, 'data map' are actually two words in English. +type DataMap struct { + Labels map[string]*DataMapMapping `json:"labels,omitempty"` +} + +type DataMapMapping struct { + Attributes []string `json:"attributes,omitempty"` +} + +func (dm *DataMap) WriteToSchema(d *schema.ResourceData) error { + // TODO: If in the future the order of the list of attributes the API + // returns becomes deterministic, this check can be removed. -aholmquist 2022-08-04 + currentDataMap := getDatamapFromResource(d) + if currentDataMap.equal(dm) { + return nil + } + + var mappings []interface{} + for label, mapping := range dm.Labels { + mappingContents := make(map[string]interface{}) + + var attributes []string + if mapping != nil { + attributes = mapping.Attributes + } + + mappingContents["label"] = label + mappingContents["attributes"] = attributes + + mappings = append(mappings, mappingContents) + } + d.SetId(d.Get("repository_id").(string)) + + return d.Set("mapping", mappings) +} + +func (dm *DataMap) ReadFromSchema(d *schema.ResourceData) error { + mappings := d.Get("mapping").(*schema.Set).List() + dm.Labels = make(map[string]*DataMapMapping) + for _, mappingIface := range mappings { + mapping := mappingIface.(map[string]interface{}) + + label := mapping["label"].(string) + var attributes []string + if mappingAtts, ok := mapping["attributes"]; ok { + for _, attributeIface := range mappingAtts.([]interface{}) { + attributes = append(attributes, attributeIface.(string)) + } + } + dm.Labels[label] = &DataMapMapping{ + Attributes: attributes, + } + } + + return nil +} + +func (dm *DataMap) equal(other *DataMap) bool { + for label, thisMapping := range dm.Labels { + if otherMapping, ok := other.Labels[label]; ok { + if !utils.ElementsMatch(thisMapping.Attributes, otherMapping.Attributes) { + return false + } + } else { + return false + } + } + return true +} + +func getDatamapFromResource(d *schema.ResourceData) DataMap { + mappings := d.Get("mapping").(*schema.Set).List() + + dataMap := DataMap{ + Labels: make(map[string]*DataMapMapping), + } + for _, mappingIface := range mappings { + mapping := mappingIface.(map[string]interface{}) + + label := mapping["label"].(string) + var attributes []string + if mappingAtts, ok := mapping["attributes"]; ok { + for _, attributeIface := range mappingAtts.([]interface{}) { + attributes = append(attributes, attributeIface.(string)) + } + } + dataMap.Labels[label] = &DataMapMapping{ + Attributes: attributes, + } + } + + return dataMap +} diff --git a/cyral/internal/repository/datamap/resource.go b/cyral/internal/repository/datamap/resource.go new file mode 100644 index 00000000..17cf6a43 --- /dev/null +++ b/cyral/internal/repository/datamap/resource.go @@ -0,0 +1,121 @@ +package datamap + +import ( + "context" + "fmt" + "net/http" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" +) + +func resourceSchema() *schema.Resource { + return &schema.Resource{ + Description: "Manages [Data Map](https://cyral.com/docs/policy/datamap).", + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ + Name: "DataMapResourceCreate", + HttpMethod: http.MethodPut, + CreateURL: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/repos/%s/datamap", + c.ControlPlane, + d.Get("repository_id").(string)) + }, + NewResourceData: func() core.ResourceData { return &DataMapRequest{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &DataMap{} }, + }, readDataMapConfig, + ), + + ReadContext: core.ReadResource(readDataMapConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ + Name: "DataMapResourceUpdate", + HttpMethod: http.MethodPut, + CreateURL: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/repos/%s/datamap", + c.ControlPlane, + d.Get("repository_id").(string)) + }, + NewResourceData: func() core.ResourceData { return &DataMapRequest{} }, + }, readDataMapConfig, + ), + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ + Name: "DataMapResourceDelete", + HttpMethod: http.MethodDelete, + CreateURL: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/repos/%s/datamap", + c.ControlPlane, + d.Get("repository_id").(string)) + }, + }, + ), + Schema: map[string]*schema.Schema{ + "repository_id": { + Description: "ID of the repository for which to configure a data map.", + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "mapping": { + Description: "Mapping of a label to a list of data locations (attributes).", + Type: schema.TypeSet, + Required: true, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "label": { + Description: "Label given to the attributes in this mapping.", + Type: schema.TypeString, + Required: true, + }, + "attributes": { + Description: "List containing the specific locations of the data within the repo, " + + "following the pattern `{SCHEMA}.{TABLE}.{ATTRIBUTE}` (ex: " + + "`[your_schema_name.your_table_name.your_attr_name]`).\n\n" + + "-> When referencing data in Dremio repository, please include the complete " + + "location in `attributes`, separating spaces by dots. For example, an attribute " + + "`my_attr` from table `my_tbl` within space `inner_space` within space `outer_space` " + + "would be referenced as `outer_space.inner_space.my_tbl.my_attr`. For more information, " + + "please see the [Policy Guide](https://cyral.com/docs/reference/policy/).", + Type: schema.TypeList, + Required: true, + // TODO: this ForceNew propagates to the parent attribute `mapping`. Therefore, any + // new mapping will force recreation. In the future, it would be good to use the + // `v1/repos/{repoID}/datamap/labels/{label}/attributes/{attribute}` endpoint to + // avoid unnecessary resource recreation. -aholmquist 2022-08-04 + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + }, + Importer: &schema.ResourceImporter{ + StateContext: func( + ctx context.Context, + d *schema.ResourceData, + m interface{}, + ) ([]*schema.ResourceData, error) { + d.Set("repository_id", d.Id()) + return []*schema.ResourceData{d}, nil + }, + }, + } +} + +var readDataMapConfig = core.ResourceOperationConfig{ + Name: "DataMapResourceRead", + HttpMethod: http.MethodGet, + CreateURL: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/repos/%s/datamap", + c.ControlPlane, + d.Get("repository_id").(string)) + }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &DataMap{} }, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Data Map"}, +} diff --git a/cyral/resource_cyral_repository_datamap_test.go b/cyral/internal/repository/datamap/resource_test.go similarity index 64% rename from cyral/resource_cyral_repository_datamap_test.go rename to cyral/internal/repository/datamap/resource_test.go index 1cb8d95c..c4441fc9 100644 --- a/cyral/resource_cyral_repository_datamap_test.go +++ b/cyral/internal/repository/datamap/resource_test.go @@ -1,11 +1,16 @@ -package cyral +package datamap_test import ( "fmt" "sort" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/datalabel" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/datamap" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/require" ) @@ -17,9 +22,9 @@ const ( ) func repositoryDatamapSampleRepositoryConfig(resName string) string { - return formatBasicRepositoryIntoConfig( - basicRepositoryResName, - accTestName(repositoryDatamapResourceName, resName), + return utils.FormatBasicRepositoryIntoConfig( + utils.BasicRepositoryResName, + utils.AccTestName(repositoryDatamapResourceName, resName), "sqlserver", "localhost", 1433, @@ -27,18 +32,18 @@ func repositoryDatamapSampleRepositoryConfig(resName string) string { } func testRepositoryDatamapCustomLabel() string { - return accTestName(repositoryDatamapResourceName, "custom-label") + return utils.AccTestName(repositoryDatamapResourceName, "custom-label") } -func initialDataMapConfigRemoveMapping() *DataMap { - return &DataMap{ - Labels: map[string]*DataMapMapping{ - predefinedLabelCCN: &DataMapMapping{ +func initialDataMapConfigRemoveMapping() *datamap.DataMap { + return &datamap.DataMap{ + Labels: map[string]*datamap.DataMapMapping{ + predefinedLabelCCN: { Attributes: []string{ "schema1.table1.col1", }, }, - predefinedLabelSSN: &DataMapMapping{ + predefinedLabelSSN: { Attributes: []string{ "schema1.table1.col2", }, @@ -47,10 +52,10 @@ func initialDataMapConfigRemoveMapping() *DataMap { } } -func updatedDataMapConfigRemoveMapping() *DataMap { - return &DataMap{ - Labels: map[string]*DataMapMapping{ - predefinedLabelSSN: &DataMapMapping{ +func updatedDataMapConfigRemoveMapping() *datamap.DataMap { + return &datamap.DataMap{ + Labels: map[string]*datamap.DataMapMapping{ + predefinedLabelSSN: &datamap.DataMapMapping{ Attributes: []string{ "schema1.table1.col2", }, @@ -59,10 +64,10 @@ func updatedDataMapConfigRemoveMapping() *DataMap { } } -func initialDataMapConfigRemoveAttribute() *DataMap { - return &DataMap{ - Labels: map[string]*DataMapMapping{ - predefinedLabelSSN: &DataMapMapping{ +func initialDataMapConfigRemoveAttribute() *datamap.DataMap { + return &datamap.DataMap{ + Labels: map[string]*datamap.DataMapMapping{ + predefinedLabelSSN: { Attributes: []string{ "a.b.c", "b.c.d", @@ -72,10 +77,10 @@ func initialDataMapConfigRemoveAttribute() *DataMap { } } -func updatedDataMapConfigRemoveAttribute() *DataMap { - return &DataMap{ - Labels: map[string]*DataMapMapping{ - predefinedLabelSSN: &DataMapMapping{ +func updatedDataMapConfigRemoveAttribute() *datamap.DataMap { + return &datamap.DataMap{ + Labels: map[string]*datamap.DataMapMapping{ + predefinedLabelSSN: { Attributes: []string{ "a.b.c", }, @@ -84,16 +89,16 @@ func updatedDataMapConfigRemoveAttribute() *DataMap { } } -func dataMapConfigWithDataLabel() (*DataMap, *DataLabel) { - return &DataMap{ - Labels: map[string]*DataMapMapping{ - testRepositoryDatamapCustomLabel(): &DataMapMapping{ +func dataMapConfigWithDataLabel() (*datamap.DataMap, *datalabel.DataLabel) { + return &datamap.DataMap{ + Labels: map[string]*datamap.DataMapMapping{ + testRepositoryDatamapCustomLabel(): { Attributes: []string{ "schema1.table1.col1", }, }, }, - }, &DataLabel{ + }, &datalabel.DataLabel{ Name: testRepositoryDatamapCustomLabel(), Description: "custom-label-description", Tags: []string{"tag1"}, @@ -104,7 +109,11 @@ func TestAccRepositoryDatamapResource(t *testing.T) { importStateResName := "cyral_repository_datamap.test_with_datalabel" resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ testRepositoryDatamapInitialConfigRemoveMapping(t), testRepositoryDatamapUpdatedConfigRemoveMapping(t), @@ -121,7 +130,7 @@ func testRepositoryDatamapInitialConfigRemoveMapping(t *testing.T) resource.Test config := initialDataMapConfigRemoveMapping() var tfConfig string tfConfig += repositoryDatamapSampleRepositoryConfig(resName) - tfConfig += formatDataMapIntoConfig(resName, basicRepositoryID, config) + tfConfig += formatDataMapIntoConfig(resName, utils.BasicRepositoryID, config) check := setupRepositoryDatamapTestFunc(t, resName, config) return resource.TestStep{Config: tfConfig, Check: check} } @@ -131,7 +140,7 @@ func testRepositoryDatamapUpdatedConfigRemoveMapping(t *testing.T) resource.Test config := updatedDataMapConfigRemoveMapping() var tfConfig string tfConfig += repositoryDatamapSampleRepositoryConfig(resName) - tfConfig += formatDataMapIntoConfig(resName, basicRepositoryID, config) + tfConfig += formatDataMapIntoConfig(resName, utils.BasicRepositoryID, config) check := setupRepositoryDatamapTestFunc(t, resName, config) return resource.TestStep{Config: tfConfig, Check: check} } @@ -141,7 +150,7 @@ func testRepositoryDatamapInitialConfigRemoveAttribute(t *testing.T) resource.Te config := initialDataMapConfigRemoveAttribute() var tfConfig string tfConfig += repositoryDatamapSampleRepositoryConfig(resName) - tfConfig += formatDataMapIntoConfig(resName, basicRepositoryID, config) + tfConfig += formatDataMapIntoConfig(resName, utils.BasicRepositoryID, config) check := setupRepositoryDatamapTestFunc(t, resName, config) return resource.TestStep{Config: tfConfig, Check: check} } @@ -151,20 +160,27 @@ func testRepositoryDatamapUpdatedConfigRemoveAttribute(t *testing.T) resource.Te config := updatedDataMapConfigRemoveAttribute() var tfConfig string tfConfig += repositoryDatamapSampleRepositoryConfig(resName) - tfConfig += formatDataMapIntoConfig(resName, basicRepositoryID, config) + tfConfig += formatDataMapIntoConfig(resName, utils.BasicRepositoryID, config) check := setupRepositoryDatamapTestFunc(t, resName, config) return resource.TestStep{Config: tfConfig, Check: check} } func testRepositoryDatamapWithDataLabel(t *testing.T) resource.TestStep { resName := "test_with_datalabel" - configDM, configDL := dataMapConfigWithDataLabel() - var tfConfig string - tfConfig += repositoryDatamapSampleRepositoryConfig(resName) - tfConfig += (formatDataMapIntoConfig(resName, basicRepositoryID, configDM) + - formatDataLabelIntoConfig(configDL.Name, configDL)) - check := setupRepositoryDatamapTestFunc(t, resName, configDM) - return resource.TestStep{Config: tfConfig, Check: check} + dataMap, dataLabel := dataMapConfigWithDataLabel() + ruleType, ruleCode, ruleStatus := "", "", "" + if dataLabel.ClassificationRule != nil { + ruleType = dataLabel.ClassificationRule.RuleType + ruleCode = dataLabel.ClassificationRule.RuleCode + ruleStatus = dataLabel.ClassificationRule.RuleStatus + } + var config string + config += repositoryDatamapSampleRepositoryConfig(resName) + config += (formatDataMapIntoConfig(resName, utils.BasicRepositoryID, dataMap) + + utils.FormatDataLabelIntoConfig(dataLabel.Name, dataLabel.Name, dataLabel.Description, + ruleType, ruleCode, ruleStatus, dataLabel.Tags)) + check := setupRepositoryDatamapTestFunc(t, resName, dataMap) + return resource.TestStep{Config: config, Check: check} } func testRepositoryDatamapImport(importStateResName string) resource.TestStep { @@ -180,14 +196,14 @@ func testRepositoryDatamapImport(importStateResName string) resource.TestStep { func setupRepositoryDatamapTestFunc( t *testing.T, resName string, - dataMap *DataMap, + dataMap *datamap.DataMap, ) resource.TestCheckFunc { resFullName := fmt.Sprintf("cyral_repository_datamap.%s", resName) testFunctions := []resource.TestCheckFunc{ resource.TestCheckResourceAttrPair( resFullName, "repository_id", - fmt.Sprintf("cyral_repository.%s", basicRepositoryResName), "id", + fmt.Sprintf("cyral_repository.%s", utils.BasicRepositoryResName), "id", ), } @@ -215,7 +231,7 @@ func setupRepositoryDatamapTestFunc( func formatDataMapIntoConfig( resName, repositoryID string, - dataMap *DataMap, + dataMap *datamap.DataMap, ) string { dependsOnStr := "" mappingsStr := "" @@ -227,7 +243,7 @@ func formatDataMapIntoConfig( mapping { label = "%s" attributes = %s - }`, label, listToStr(mapping.Attributes)) + }`, label, utils.ListToStr(mapping.Attributes)) if label == testRepositoryDatamapCustomLabel() { // If there is a custom label in the configuration, we @@ -235,7 +251,7 @@ func formatDataMapIntoConfig( // label cannot be deleted. The depends_on Terraform // meta-argument forces the right deletion order. dependsOnStr = fmt.Sprintf("depends_on = [%s]", - datalabelConfigResourceFullName(label)) + utils.DatalabelConfigResourceFullName(label)) } } @@ -251,7 +267,7 @@ func formatDataMapIntoConfig( // dataMapSortedLabels exists to allow construction and checking of terraform // configurations with the data map following the same order of the mappings. -func dataMapSortedLabels(dataMap *DataMap) []string { +func dataMapSortedLabels(dataMap *datamap.DataMap) []string { var labels []string for label, _ := range dataMap.Labels { labels = append(labels, label) diff --git a/cyral/internal/repository/datamap/schema_loader.go b/cyral/internal/repository/datamap/schema_loader.go new file mode 100644 index 00000000..647b5bf1 --- /dev/null +++ b/cyral/internal/repository/datamap/schema_loader.go @@ -0,0 +1,26 @@ +package datamap + +import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/core" +) + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "datamap" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: "cyral_repository_datamap", + Type: core.ResourceSchemaType, + Schema: resourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/resource_cyral_repository_network_access_policy.go b/cyral/internal/repository/network/resource_cyral_repository_network_access_policy.go similarity index 77% rename from cyral/resource_cyral_repository_network_access_policy.go rename to cyral/internal/repository/network/resource_cyral_repository_network_access_policy.go index b88920b4..81f80a1b 100644 --- a/cyral/resource_cyral_repository_network_access_policy.go +++ b/cyral/internal/repository/network/resource_cyral_repository_network_access_policy.go @@ -1,11 +1,14 @@ -package cyral +package network import ( "context" "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -18,8 +21,8 @@ const ( func repositoryTypesNetworkShield() []string { return []string{ - SQLServer, - Oracle, + repository.SQLServer, + repository.Oracle, } } @@ -69,8 +72,8 @@ func (nap *NetworkAccessPolicy) ReadFromSchema(d *schema.ResourceData) error { NetworkAccessRule{ Name: networkAccessRuleMap["name"].(string), Description: networkAccessRuleMap["description"].(string), - DBAccounts: getStrList(networkAccessRuleMap, "db_accounts"), - SourceIPs: getStrList(networkAccessRuleMap, "source_ips"), + DBAccounts: utils.GetStrList(networkAccessRuleMap, "db_accounts"), + SourceIPs: utils.GetStrList(networkAccessRuleMap, "source_ips"), }) } @@ -96,70 +99,70 @@ func (nap *NetworkAccessPolicy) WriteToSchema(d *schema.ResourceData) error { return d.Set("network_access_rule", networkAccessRules) } -func createRepositoryNetworkAccessPolicy() ResourceOperationConfig { - return ResourceOperationConfig{ +func createRepositoryNetworkAccessPolicy() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "RepositoryNetworkAccessPolicyCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf(repositoryNetworkAccessPolicyURLFormat, c.ControlPlane, d.Get("repository_id")) }, - NewResourceData: func() ResourceData { return &NetworkAccessPolicy{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &NetworkAccessPolicyUpsertResp{} }, + NewResourceData: func() core.ResourceData { return &NetworkAccessPolicy{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &NetworkAccessPolicyUpsertResp{} }, } } -func readRepositoryNetworkAccessPolicy() ResourceOperationConfig { - return ResourceOperationConfig{ +func readRepositoryNetworkAccessPolicy() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "RepositoryNetworkAccessPolicyRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf(repositoryNetworkAccessPolicyURLFormat, c.ControlPlane, d.Get("repository_id")) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &NetworkAccessPolicy{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "Repository network access policy"}, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &NetworkAccessPolicy{} }, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Repository network access policy"}, } } -func updateRepositoryNetworkAccessPolicy() ResourceOperationConfig { - return ResourceOperationConfig{ +func updateRepositoryNetworkAccessPolicy() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "RepositoryNetworkAccessPolicyUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf(repositoryNetworkAccessPolicyURLFormat, c.ControlPlane, d.Get("repository_id")) }, - NewResourceData: func() ResourceData { return &NetworkAccessPolicy{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &NetworkAccessPolicyUpsertResp{} }, + NewResourceData: func() core.ResourceData { return &NetworkAccessPolicy{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &NetworkAccessPolicyUpsertResp{} }, } } -func deleteRepositoryNetworkAccessPolicy() ResourceOperationConfig { - return ResourceOperationConfig{ +func deleteRepositoryNetworkAccessPolicy() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "RepositoryNetworkAccessPolicyDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf(repositoryNetworkAccessPolicyURLFormat, c.ControlPlane, d.Get("repository_id")) }, - RequestErrorHandler: &DeleteIgnoreHttpNotFound{resName: "Network Access Policy"}, + RequestErrorHandler: &core.DeleteIgnoreHttpNotFound{ResName: "Network Access Policy"}, } } -func resourceRepositoryNetworkAccessPolicy() *schema.Resource { +func ResourceRepositoryNetworkAccessPolicy() *schema.Resource { return &schema.Resource{ Description: "Manages the network access policy of a repository. Network access policies are" + " also known as the [Network Shield](https://cyral.com/docs/manage-repositories/network-shield/)." + " This feature is supported for the following repository types:" + - supportedTypesMarkdown(repositoryTypesNetworkShield()) + + utils.SupportedValuesAsMarkdown(repositoryTypesNetworkShield()) + "\n\n-> **Note** If you also use the resource `cyral_repository_conf_auth` for the same repository," + " create a `depends_on` relationship from this resource to the `cyral_repository_conf_auth` to" + " avoid errors when running `terraform destroy`.", - CreateContext: CreateResource(createRepositoryNetworkAccessPolicy(), readRepositoryNetworkAccessPolicy()), - ReadContext: ReadResource(readRepositoryNetworkAccessPolicy()), - UpdateContext: UpdateResource(updateRepositoryNetworkAccessPolicy(), readRepositoryNetworkAccessPolicy()), - DeleteContext: DeleteResource(deleteRepositoryNetworkAccessPolicy()), + CreateContext: core.CreateResource(createRepositoryNetworkAccessPolicy(), readRepositoryNetworkAccessPolicy()), + ReadContext: core.ReadResource(readRepositoryNetworkAccessPolicy()), + UpdateContext: core.UpdateResource(updateRepositoryNetworkAccessPolicy(), readRepositoryNetworkAccessPolicy()), + DeleteContext: core.DeleteResource(deleteRepositoryNetworkAccessPolicy()), Schema: map[string]*schema.Schema{ "repository_id": { diff --git a/cyral/resource_cyral_repository_network_access_policy_test.go b/cyral/internal/repository/network/resource_cyral_repository_network_access_policy_test.go similarity index 77% rename from cyral/resource_cyral_repository_network_access_policy_test.go rename to cyral/internal/repository/network/resource_cyral_repository_network_access_policy_test.go index eb5d0839..f4d11835 100644 --- a/cyral/resource_cyral_repository_network_access_policy_test.go +++ b/cyral/internal/repository/network/resource_cyral_repository_network_access_policy_test.go @@ -1,10 +1,13 @@ -package cyral +package network_test import ( "fmt" "strconv" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/network" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -14,16 +17,16 @@ const ( func TestAccRepositoryNetworkAccessPolicyResource(t *testing.T) { // In these tests, the resources should be recreated every time. - emptyFieldsNotEnabled := &NetworkAccessPolicy{ + emptyFieldsNotEnabled := &network.NetworkAccessPolicy{ Enabled: false, - NetworkAccessRules: NetworkAccessRules{ + NetworkAccessRules: network.NetworkAccessRules{ RulesBlockAccess: false, - Rules: []NetworkAccessRule{}, + Rules: []network.NetworkAccessRule{}, }, } - emptyFieldsExceptName := &NetworkAccessPolicy{ - NetworkAccessRules: NetworkAccessRules{ - Rules: []NetworkAccessRule{ + emptyFieldsExceptName := &network.NetworkAccessPolicy{ + NetworkAccessRules: network.NetworkAccessRules{ + Rules: []network.NetworkAccessRule{ { Name: "name-1", }, @@ -36,9 +39,9 @@ func TestAccRepositoryNetworkAccessPolicyResource(t *testing.T) { "empty_fields_except_name", emptyFieldsExceptName, nil) // In these tests, the resources should be updated in-place - fullRule := &NetworkAccessPolicy{ - NetworkAccessRules: NetworkAccessRules{ - Rules: []NetworkAccessRule{ + fullRule := &network.NetworkAccessPolicy{ + NetworkAccessRules: network.NetworkAccessRules{ + Rules: []network.NetworkAccessRule{ { Name: "name-2", Description: "description-2", @@ -48,9 +51,9 @@ func TestAccRepositoryNetworkAccessPolicyResource(t *testing.T) { }, }, } - fullRuleUpdated := &NetworkAccessPolicy{ - NetworkAccessRules: NetworkAccessRules{ - Rules: []NetworkAccessRule{ + fullRuleUpdated := &network.NetworkAccessPolicy{ + NetworkAccessRules: network.NetworkAccessRules{ + Rules: []network.NetworkAccessRule{ { Name: "name-3", Description: "description-3", @@ -60,9 +63,9 @@ func TestAccRepositoryNetworkAccessPolicyResource(t *testing.T) { }, }, } - fullRuleUpdatedAdditionalRule := &NetworkAccessPolicy{ - NetworkAccessRules: NetworkAccessRules{ - Rules: []NetworkAccessRule{ + fullRuleUpdatedAdditionalRule := &network.NetworkAccessPolicy{ + NetworkAccessRules: network.NetworkAccessRules{ + Rules: []network.NetworkAccessRule{ { Name: "name-3", Description: "description-3", @@ -88,7 +91,7 @@ func TestAccRepositoryNetworkAccessPolicyResource(t *testing.T) { importResourceName := "cyral_repository_network_access_policy.full_rule" resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ // Recreation tests emptyFieldsNotEnabledTest, @@ -111,7 +114,7 @@ func TestAccRepositoryNetworkAccessPolicyResource(t *testing.T) { func setupRepositoryNetworkAccessPolicyTest( resName string, - nap *NetworkAccessPolicy, + nap *network.NetworkAccessPolicy, dbAccounts []string, ) resource.TestStep { return resource.TestStep{ @@ -122,7 +125,7 @@ func setupRepositoryNetworkAccessPolicyTest( func setupRepositoryNetworkAccessPolicyConfig( resName string, - nap *NetworkAccessPolicy, + nap *network.NetworkAccessPolicy, dbAccounts []string, ) string { var config string @@ -130,9 +133,9 @@ func setupRepositoryNetworkAccessPolicyConfig( // Repository repoResName := "test_repo" repoID := fmt.Sprintf("cyral_repository.%s.id", repoResName) - config += formatBasicRepositoryIntoConfig( + config += utils.FormatBasicRepositoryIntoConfig( repoResName, - accTestName(repositoryNetworkAccessPolicyResourceName, resName), + utils.AccTestName(repositoryNetworkAccessPolicyResourceName, resName), "sqlserver", "my.host.com", 1433, @@ -146,7 +149,7 @@ func setupRepositoryNetworkAccessPolicyConfig( func setupRepositoryNetworkAccessPolicyCheck( resName string, - nap *NetworkAccessPolicy, + nap *network.NetworkAccessPolicy, ) resource.TestCheckFunc { resFullName := fmt.Sprintf("cyral_repository_network_access_policy.%s", resName) @@ -191,7 +194,7 @@ func setupRepositoryNetworkAccessPolicyCheck( } func formatNetworkAccessPolicyIntoConfig( - resName, repositoryID string, nap *NetworkAccessPolicy, dependsOn []string, + resName, repositoryID string, nap *network.NetworkAccessPolicy, dependsOn []string, ) string { var narStr string for _, nar := range nap.Rules { @@ -201,8 +204,8 @@ func formatNetworkAccessPolicyIntoConfig( description = "%s" db_accounts = %s source_ips = %s - }`, nar.Name, nar.Description, listToStr(nar.DBAccounts), - listToStr(nar.SourceIPs)) + }`, nar.Name, nar.Description, utils.ListToStr(nar.DBAccounts), + utils.ListToStr(nar.SourceIPs)) } config := fmt.Sprintf(` @@ -213,7 +216,7 @@ func formatNetworkAccessPolicyIntoConfig( %s depends_on = %s }`, resName, repositoryID, nap.Enabled, nap.NetworkAccessRules.RulesBlockAccess, - narStr, listToStrNoQuotes(dependsOn)) + narStr, utils.ListToStrNoQuotes(dependsOn)) return config } diff --git a/cyral/resource_cyral_repository.go b/cyral/internal/repository/resource_cyral_repository.go similarity index 92% rename from cyral/resource_cyral_repository.go rename to cyral/internal/repository/resource_cyral_repository.go index 7be80dfb..f6003dd9 100644 --- a/cyral/resource_cyral_repository.go +++ b/cyral/internal/repository/resource_cyral_repository.go @@ -1,10 +1,12 @@ -package cyral +package repository import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) @@ -48,7 +50,7 @@ const ( SQLServer = "sqlserver" ) -func repositoryTypes() []string { +func RepositoryTypes() []string { return []string{ Denodo, Dremio, @@ -142,9 +144,9 @@ func (r *RepoInfo) ReadFromSchema(d *schema.ResourceData) error { r.ConnDrainingFromInterface(d.Get(RepoConnDrainingKey).(*schema.Set).List()) var mongoDBSettings = d.Get(RepoMongoDBSettingsKey).(*schema.Set).List() if r.Type == MongoDB && (mongoDBSettings == nil || len(mongoDBSettings) == 0) { - return fmt.Errorf("'%s' block must be provided when '%s=%s'", RepoMongoDBSettingsKey, TypeKey, MongoDB) + return fmt.Errorf("'%s' block must be provided when '%s=%s'", RepoMongoDBSettingsKey, utils.TypeKey, MongoDB) } else if r.Type != MongoDB && len(mongoDBSettings) > 0 { - return fmt.Errorf("'%s' block is only allowed when '%s=%s'", RepoMongoDBSettingsKey, TypeKey, MongoDB) + return fmt.Errorf("'%s' block is only allowed when '%s=%s'", RepoMongoDBSettingsKey, utils.TypeKey, MongoDB) } return r.MongoDBSettingsFromInterface(mongoDBSettings) } @@ -268,7 +270,7 @@ func (r *RepoInfo) MongoDBSettingsFromInterface(i []interface{}) error { return nil } -var ReadRepositoryConfig = ResourceOperationConfig{ +var ReadRepositoryConfig = core.ResourceOperationConfig{ Name: "RepositoryRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -278,19 +280,19 @@ var ReadRepositoryConfig = ResourceOperationConfig{ d.Id(), ) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &GetRepoByIDResponse{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "Repository"}, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Repository"}, } -func resourceRepository() *schema.Resource { +func ResourceRepository() *schema.Resource { return &schema.Resource{ Description: "Manages [repositories](https://cyral.com/docs/manage-repositories/repo-track)." + "\n\nSee also [Cyral Repository Configuration Module](https://github.com/cyralinc/terraform-cyral-repository-config)." + "\nThis module provides the repository configuration options as shown in Cyral UI.", - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "RepositoryCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -299,18 +301,18 @@ func resourceRepository() *schema.Resource { c.ControlPlane, ) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return &RepoInfo{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { - return &IDBasedResponse{} + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { + return &core.IDBasedResponse{} }, }, ReadRepositoryConfig, ), - ReadContext: ReadResource(ReadRepositoryConfig), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadRepositoryConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "RepositoryUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -320,14 +322,14 @@ func resourceRepository() *schema.Resource { d.Id(), ) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return &RepoInfo{} }, }, ReadRepositoryConfig, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "RepositoryDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -347,11 +349,11 @@ func resourceRepository() *schema.Resource { Computed: true, }, RepoTypeKey: { - Description: "Repository type. List of supported types:" + supportedTypesMarkdown(repositoryTypes()), + Description: "Repository type. List of supported types:" + utils.SupportedValuesAsMarkdown(RepositoryTypes()), Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validation.StringInSlice(repositoryTypes(), false), + ValidateFunc: validation.StringInSlice(RepositoryTypes(), false), }, RepoNameKey: { Description: "Repository name that will be used internally in the control plane (ex: `your_repo_name`).", @@ -441,7 +443,7 @@ func resourceRepository() *schema.Resource { Optional: true, }, RepoMongoDBServerTypeKey: { - Description: "Type of the MongoDB server. Allowed values: " + supportedTypesMarkdown(mongoServerTypes()) + + Description: "Type of the MongoDB server. Allowed values: " + utils.SupportedValuesAsMarkdown(mongoServerTypes()) + "\n\n The following conditions apply:\n" + " - If `" + Sharded + "` and `" + RepoMongoDBSRVRecordName + "` *not* provided, then all `" + RepoNodesKey + "` blocks must be static (see [`" + RepoNodeDynamicKey + "`](#" + RepoNodeDynamicKey + ")).\n" + diff --git a/cyral/resource_cyral_repository_test.go b/cyral/internal/repository/resource_cyral_repository_test.go similarity index 68% rename from cyral/resource_cyral_repository_test.go rename to cyral/internal/repository/resource_cyral_repository_test.go index 9ac13ae1..7ffd9752 100644 --- a/cyral/resource_cyral_repository_test.go +++ b/cyral/internal/repository/resource_cyral_repository_test.go @@ -1,95 +1,95 @@ -package cyral +package repository_test import ( "fmt" "strconv" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -const ( - repositoryResourceName = "repository" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) var ( - initialRepoConfig = RepoInfo{ - Name: accTestName(repositoryResourceName, "repo"), - Type: MongoDB, + initialRepoConfig = repository.RepoInfo{ + Name: utils.AccTestName(utils.RepositoryResourceName, "repo"), + Type: repository.MongoDB, Labels: []string{"rds", "us-east-2"}, - RepoNodes: []*RepoNode{ + RepoNodes: []*repository.RepoNode{ { Host: "mongo.local", Port: 3333, }, }, - MongoDBSettings: &MongoDBSettings{ - ServerType: Standalone, + MongoDBSettings: &repository.MongoDBSettings{ + ServerType: repository.Standalone, }, } - updatedRepoConfig = RepoInfo{ - Name: accTestName(repositoryResourceName, "repo-updated"), - Type: MongoDB, + updatedRepoConfig = repository.RepoInfo{ + Name: utils.AccTestName(utils.RepositoryResourceName, "repo-updated"), + Type: repository.MongoDB, Labels: []string{"rds", "us-east-1"}, - RepoNodes: []*RepoNode{ + RepoNodes: []*repository.RepoNode{ { Host: "mongo.local", Port: 3334, }, }, - MongoDBSettings: &MongoDBSettings{ - ServerType: Standalone, + MongoDBSettings: &repository.MongoDBSettings{ + ServerType: repository.Standalone, }, } - emptyConnDrainingConfig = RepoInfo{ - Name: accTestName(repositoryResourceName, "repo-empty-conn-draining"), - Type: MongoDB, - ConnParams: &ConnParams{ - ConnDraining: &ConnDraining{}, + emptyConnDrainingConfig = repository.RepoInfo{ + Name: utils.AccTestName(utils.RepositoryResourceName, "repo-empty-conn-draining"), + Type: repository.MongoDB, + ConnParams: &repository.ConnParams{ + ConnDraining: &repository.ConnDraining{}, }, - RepoNodes: []*RepoNode{ + RepoNodes: []*repository.RepoNode{ { Host: "mongo-cluster.local", Port: 27017, }, }, - MongoDBSettings: &MongoDBSettings{ - ServerType: Standalone, + MongoDBSettings: &repository.MongoDBSettings{ + ServerType: repository.Standalone, }, } - connDrainingConfig = RepoInfo{ - Name: accTestName(repositoryResourceName, "repo-conn-draining"), - Type: MongoDB, - ConnParams: &ConnParams{ - ConnDraining: &ConnDraining{ + connDrainingConfig = repository.RepoInfo{ + Name: utils.AccTestName(utils.RepositoryResourceName, "repo-conn-draining"), + Type: repository.MongoDB, + ConnParams: &repository.ConnParams{ + ConnDraining: &repository.ConnDraining{ Auto: true, WaitTime: 20, }, }, - RepoNodes: []*RepoNode{ + RepoNodes: []*repository.RepoNode{ { Host: "mongo-cluster.local", Port: 27017, }, }, - MongoDBSettings: &MongoDBSettings{ - ServerType: Standalone, + MongoDBSettings: &repository.MongoDBSettings{ + ServerType: repository.Standalone, }, } - mixedMultipleNodesConfig = RepoInfo{ - Name: accTestName(repositoryResourceName, "repo-mixed-multi-node"), - Type: MongoDB, - ConnParams: &ConnParams{ - ConnDraining: &ConnDraining{ + mixedMultipleNodesConfig = repository.RepoInfo{ + Name: utils.AccTestName(utils.RepositoryResourceName, "repo-mixed-multi-node"), + Type: repository.MongoDB, + ConnParams: &repository.ConnParams{ + ConnDraining: &repository.ConnDraining{ Auto: true, WaitTime: 20, }, }, - RepoNodes: []*RepoNode{ + RepoNodes: []*repository.RepoNode{ { Name: "node1", Host: "mongo-cluster.local.node1", @@ -113,16 +113,16 @@ var ( Dynamic: true, }, }, - MongoDBSettings: &MongoDBSettings{ + MongoDBSettings: &repository.MongoDBSettings{ ReplicaSetName: "some-replica-set", - ServerType: ReplicaSet, + ServerType: repository.ReplicaSet, }, } - allRepoNodesAreDynamic = RepoInfo{ - Name: accTestName(repositoryResourceName, "repo-all-repo-nodes-are-dynamic"), + allRepoNodesAreDynamic = repository.RepoInfo{ + Name: utils.AccTestName(utils.RepositoryResourceName, "repo-all-repo-nodes-are-dynamic"), Type: "mongodb", - RepoNodes: []*RepoNode{ + RepoNodes: []*repository.RepoNode{ { Dynamic: true, }, @@ -133,7 +133,7 @@ var ( Dynamic: true, }, }, - MongoDBSettings: &MongoDBSettings{ + MongoDBSettings: &repository.MongoDBSettings{ ReplicaSetName: "myReplicaSet", ServerType: "replicaset", SRVRecordName: "mySRVRecord", @@ -164,7 +164,11 @@ func TestAccRepositoryResource(t *testing.T) { } resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ initial, update, @@ -177,14 +181,14 @@ func TestAccRepositoryResource(t *testing.T) { }) } -func setupRepositoryTest(repo RepoInfo, resName string) resource.TestStep { +func setupRepositoryTest(repo repository.RepoInfo, resName string) resource.TestStep { return resource.TestStep{ Config: repoAsConfig(repo, resName), Check: repoCheckFuctions(repo, resName), } } -func repoCheckFuctions(repo RepoInfo, resName string) resource.TestCheckFunc { +func repoCheckFuctions(repo repository.RepoInfo, resName string) resource.TestCheckFunc { resourceFullName := fmt.Sprintf("cyral_repository.%s", resName) checkFuncs := []resource.TestCheckFunc{ @@ -230,7 +234,7 @@ func repoCheckFuctions(repo RepoInfo, resName string) resource.TestCheckFunc { } if repo.MongoDBSettings != nil { - if repo.MongoDBSettings.ServerType == ReplicaSet { + if repo.MongoDBSettings.ServerType == repository.ReplicaSet { checkFuncs = append(checkFuncs, []resource.TestCheckFunc{ resource.TestCheckResourceAttr(resourceFullName, "mongodb_settings.0.replica_set_name", @@ -251,12 +255,12 @@ func repoCheckFuctions(repo RepoInfo, resName string) resource.TestCheckFunc { return resource.ComposeTestCheckFunc(checkFuncs...) } -func repoAsConfig(repo RepoInfo, resName string) string { +func repoAsConfig(repo repository.RepoInfo, resName string) string { config := fmt.Sprintf(` resource "cyral_repository" "%s" { type = "%s" name = "%s" - labels = %s`, resName, repo.Type, repo.Name, listToStr(repo.Labels)) + labels = %s`, resName, repo.Type, repo.Name, utils.ListToStr(repo.Labels)) if repo.ConnParams != nil { config += fmt.Sprintf(` diff --git a/cyral/resource_cyral_repository_user_account.go b/cyral/internal/repository/useraccount/resource_cyral_repository_user_account.go similarity index 93% rename from cyral/resource_cyral_repository_user_account.go rename to cyral/internal/repository/useraccount/resource_cyral_repository_user_account.go index 73b01f58..e0a33bc3 100644 --- a/cyral/resource_cyral_repository_user_account.go +++ b/cyral/internal/repository/useraccount/resource_cyral_repository_user_account.go @@ -1,11 +1,13 @@ -package cyral +package useraccount import ( "context" "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -82,7 +84,7 @@ type CreateUserAccountResponse struct { } func (resp *CreateUserAccountResponse) WriteToSchema(d *schema.ResourceData) error { - d.SetId(marshalComposedID( + d.SetId(utils.MarshalComposedID( []string{ d.Get("repository_id").(string), resp.UserAccountID, @@ -298,11 +300,11 @@ func (userAccount *UserAccountResource) ReadFromSchema(d *schema.ResourceData) e return nil } -var ReadRepositoryUserAccountConfig = ResourceOperationConfig{ +var ReadRepositoryUserAccountConfig = core.ResourceOperationConfig{ Name: "RepositoryUserAccountRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { - ids, err := unmarshalComposedID(d.Id(), "/", 2) + ids, err := utils.UnMarshalComposedID(d.Id(), "/", 2) if err != nil { panic(fmt.Errorf("Unable to unmarshal composed id: %w", err)) } @@ -315,21 +317,21 @@ var ReadRepositoryUserAccountConfig = ResourceOperationConfig{ userAccountID, ) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &UserAccountResource{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "User account"}, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "User account"}, } -func resourceRepositoryUserAccount() *schema.Resource { +func ResourceRepositoryUserAccount() *schema.Resource { authSchemeTypesFullScopes := make([]string, 0, len(allAuthSchemes)) for _, authType := range allAuthSchemes { authSchemeTypesFullScopes = append(authSchemeTypesFullScopes, fmt.Sprintf("auth_scheme.0.%s", authType)) } return &schema.Resource{ - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "RepositoryUserAccountCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -339,22 +341,22 @@ func resourceRepositoryUserAccount() *schema.Resource { d.Get("repository_id").(string), ) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return &UserAccountResource{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &CreateUserAccountResponse{} }, }, ReadRepositoryUserAccountConfig, ), - ReadContext: ReadResource(ReadRepositoryUserAccountConfig), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadRepositoryUserAccountConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "RepositoryUserAccountUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { - ids, err := unmarshalComposedID(d.Id(), "/", 2) + ids, err := utils.UnMarshalComposedID(d.Id(), "/", 2) if err != nil { panic(fmt.Errorf("Unable to unmarshal composed id: %w", err)) } @@ -367,18 +369,18 @@ func resourceRepositoryUserAccount() *schema.Resource { userAccountID, ) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return &UserAccountResource{} }, }, ReadRepositoryUserAccountConfig, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "RepositoryUserAccountDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { - ids, err := unmarshalComposedID(d.Id(), "/", 2) + ids, err := utils.UnMarshalComposedID(d.Id(), "/", 2) if err != nil { panic(fmt.Errorf("Unable to unmarshal composed id: %w", err)) } @@ -400,7 +402,7 @@ func resourceRepositoryUserAccount() *schema.Resource { d *schema.ResourceData, i interface{}, ) ([]*schema.ResourceData, error) { - ids, err := unmarshalComposedID(d.Id(), "/", 2) + ids, err := utils.UnMarshalComposedID(d.Id(), "/", 2) if err != nil { return nil, fmt.Errorf( "failed to unmarshal ID: %v", @@ -476,7 +478,7 @@ func resourceRepositoryUserAccount() *schema.Resource { "auth_scheme": { Description: "Credential option. List of supported types: " + - supportedTypesMarkdown(allAuthSchemes), + utils.SupportedValuesAsMarkdown(allAuthSchemes), Required: true, Type: schema.TypeList, MaxItems: 1, diff --git a/cyral/resource_cyral_repository_user_account_test.go b/cyral/internal/repository/useraccount/resource_cyral_repository_user_account_test.go similarity index 78% rename from cyral/resource_cyral_repository_user_account_test.go rename to cyral/internal/repository/useraccount/resource_cyral_repository_user_account_test.go index b75f5aa6..74533733 100644 --- a/cyral/resource_cyral_repository_user_account_test.go +++ b/cyral/internal/repository/useraccount/resource_cyral_repository_user_account_test.go @@ -1,10 +1,13 @@ -package cyral +package useraccount_test import ( "fmt" "strconv" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/useraccount" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -13,9 +16,9 @@ const ( ) func repositoryUserAccountRepositoryConfig() string { - return formatBasicRepositoryIntoConfig( - basicRepositoryResName, - accTestName(repositoryUserAccountResourceName, "main-repo"), + return utils.FormatBasicRepositoryIntoConfig( + utils.BasicRepositoryResName, + utils.AccTestName(repositoryUserAccountResourceName, "main-repo"), "mongodb", "mongodb.local", 27017, @@ -24,56 +27,56 @@ func repositoryUserAccountRepositoryConfig() string { func TestAccRepositoryUserAccountResource(t *testing.T) { // Upgrade test - onlyRequiredFields := UserAccountResource{ + onlyRequiredFields := useraccount.UserAccountResource{ Name: "name-1", - AuthScheme: &AuthScheme{ - AWSSecretsManager: &AuthSchemeAWSSecretsManager{ + AuthScheme: &useraccount.AuthScheme{ + AWSSecretsManager: &useraccount.AuthSchemeAWSSecretsManager{ SecretArn: "secret-arn-1", }, }, } - anotherName := UserAccountResource{ + anotherName := useraccount.UserAccountResource{ Name: "name-2", - AuthScheme: &AuthScheme{ - AWSSecretsManager: &AuthSchemeAWSSecretsManager{ + AuthScheme: &useraccount.AuthScheme{ + AWSSecretsManager: &useraccount.AuthSchemeAWSSecretsManager{ SecretArn: "secret-arn-1", }, }, } - anotherAuthScheme := UserAccountResource{ + anotherAuthScheme := useraccount.UserAccountResource{ Name: "name-2", - AuthScheme: &AuthScheme{ - GCPSecretManager: &AuthSchemeGCPSecretManager{ + AuthScheme: &useraccount.AuthScheme{ + GCPSecretManager: &useraccount.AuthSchemeGCPSecretManager{ SecretName: "secret-name-1", }, }, } - withOptionalFields := UserAccountResource{ + withOptionalFields := useraccount.UserAccountResource{ Name: "name-2", AuthDatabaseName: "auth-database-name-1", - Config: &UserAccountConfig{ - Approval: &ApprovalConfig{ + Config: &useraccount.UserAccountConfig{ + Approval: &useraccount.ApprovalConfig{ AutomaticGrant: true, MaxAutomaticGrantDuration: "1234s", }, }, - AuthScheme: &AuthScheme{ - GCPSecretManager: &AuthSchemeGCPSecretManager{ + AuthScheme: &useraccount.AuthScheme{ + GCPSecretManager: &useraccount.AuthSchemeGCPSecretManager{ SecretName: "secret-name-1", }, }, } - withOptionalFieldsUpdated := UserAccountResource{ + withOptionalFieldsUpdated := useraccount.UserAccountResource{ Name: "name-3", AuthDatabaseName: "auth-database-name-2", - Config: &UserAccountConfig{ - Approval: &ApprovalConfig{ + Config: &useraccount.UserAccountConfig{ + Approval: &useraccount.ApprovalConfig{ AutomaticGrant: true, MaxAutomaticGrantDuration: "4321s", }, }, - AuthScheme: &AuthScheme{ - GCPSecretManager: &AuthSchemeGCPSecretManager{ + AuthScheme: &useraccount.AuthScheme{ + GCPSecretManager: &useraccount.AuthSchemeGCPSecretManager{ SecretName: "secret-name-2", }, }, @@ -90,60 +93,60 @@ func TestAccRepositoryUserAccountResource(t *testing.T) { "upgrade_test", withOptionalFieldsUpdated) // Tests covering all auth scheme types - awsIAM := UserAccountResource{ + awsIAM := useraccount.UserAccountResource{ Name: "aws-iam-useracc", - AuthScheme: &AuthScheme{ - AWSIAM: &AuthSchemeAWSIAM{ + AuthScheme: &useraccount.AuthScheme{ + AWSIAM: &useraccount.AuthSchemeAWSIAM{ RoleARN: "role-arn-1", }, }, } - awsSecretsManager := UserAccountResource{ + awsSecretsManager := useraccount.UserAccountResource{ Name: "aws-sm-useracc", - AuthScheme: &AuthScheme{ - AWSSecretsManager: &AuthSchemeAWSSecretsManager{ + AuthScheme: &useraccount.AuthScheme{ + AWSSecretsManager: &useraccount.AuthSchemeAWSSecretsManager{ SecretArn: "secret-arn-1", }, }, } - cyralStorage := UserAccountResource{ + cyralStorage := useraccount.UserAccountResource{ Name: "cyral-storage-useracc", - AuthScheme: &AuthScheme{ - CyralStorage: &AuthSchemeCyralStorage{ + AuthScheme: &useraccount.AuthScheme{ + CyralStorage: &useraccount.AuthSchemeCyralStorage{ Password: "password-1", }, }, } - hashicorpVault := UserAccountResource{ + hashicorpVault := useraccount.UserAccountResource{ Name: "hashicorp-vault-useracc", - AuthScheme: &AuthScheme{ - HashicorpVault: &AuthSchemeHashicorpVault{ + AuthScheme: &useraccount.AuthScheme{ + HashicorpVault: &useraccount.AuthSchemeHashicorpVault{ Path: "path-1", IsDynamicUserAccount: true, }, }, } - environmentVariable := UserAccountResource{ + environmentVariable := useraccount.UserAccountResource{ Name: "env-var-useracc", - AuthScheme: &AuthScheme{ - EnvironmentVariable: &AuthSchemeEnvironmentVariable{ + AuthScheme: &useraccount.AuthScheme{ + EnvironmentVariable: &useraccount.AuthSchemeEnvironmentVariable{ VariableName: "variable-name-1", }, }, } - kubernetesSecret := UserAccountResource{ + kubernetesSecret := useraccount.UserAccountResource{ Name: "kubesecrets-useracc", - AuthScheme: &AuthScheme{ - KubernetesSecret: &AuthSchemeKubernetesSecret{ + AuthScheme: &useraccount.AuthScheme{ + KubernetesSecret: &useraccount.AuthSchemeKubernetesSecret{ SecretName: "secret-name-1", SecretKey: "secret-key-1", }, }, } - gcpSecretManager := UserAccountResource{ + gcpSecretManager := useraccount.UserAccountResource{ Name: "gcp-useracc", - AuthScheme: &AuthScheme{ - GCPSecretManager: &AuthSchemeGCPSecretManager{ + AuthScheme: &useraccount.AuthScheme{ + GCPSecretManager: &useraccount.AuthSchemeGCPSecretManager{ SecretName: "secret-name-1", }, }, @@ -195,7 +198,7 @@ func TestAccRepositoryUserAccountResource(t *testing.T) { } resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ // Update tests onlyRequiredFieldsTest, @@ -222,7 +225,7 @@ func TestAccRepositoryUserAccountResource(t *testing.T) { }) } -func setupRepositoryUserAccountTest(resName string, userAccount UserAccountResource) resource.TestStep { +func setupRepositoryUserAccountTest(resName string, userAccount useraccount.UserAccountResource) resource.TestStep { return resource.TestStep{ Config: repositoryUserAccountRepositoryConfig() + setupRepositoryUserAccountConfig(resName, userAccount), @@ -230,7 +233,7 @@ func setupRepositoryUserAccountTest(resName string, userAccount UserAccountResou } } -func setupRepositoryUserAccountCheck(resName string, userAccount UserAccountResource) resource.TestCheckFunc { +func setupRepositoryUserAccountCheck(resName string, userAccount useraccount.UserAccountResource) resource.TestCheckFunc { resFullName := fmt.Sprintf("cyral_repository_user_account.%s", resName) var checkFuncs []resource.TestCheckFunc @@ -239,7 +242,7 @@ func setupRepositoryUserAccountCheck(resName string, userAccount UserAccountReso checkFuncs = append(checkFuncs, []resource.TestCheckFunc{ resource.TestCheckResourceAttrPair( resFullName, "repository_id", - fmt.Sprintf("cyral_repository.%s", basicRepositoryResName), "id"), + fmt.Sprintf("cyral_repository.%s", utils.BasicRepositoryResName), "id"), resource.TestCheckResourceAttr(resFullName, "name", userAccount.Name), resource.TestCheckResourceAttr(resFullName, @@ -319,7 +322,7 @@ func setupRepositoryUserAccountCheck(resName string, userAccount UserAccountReso return resource.ComposeTestCheckFunc(checkFuncs...) } -func setupRepositoryUserAccountConfig(resName string, userAccount UserAccountResource) string { +func setupRepositoryUserAccountConfig(resName string, userAccount useraccount.UserAccountResource) string { var config string var authSchemeStr string @@ -384,7 +387,7 @@ func setupRepositoryUserAccountConfig(resName string, userAccount UserAccountRes %s } %s - }`, resName, basicRepositoryID, userAccount.Name, + }`, resName, utils.BasicRepositoryID, userAccount.Name, userAccount.AuthDatabaseName, authSchemeStr, approvalConfigStr) return config diff --git a/cyral/data_source_cyral_role.go b/cyral/internal/role/data_source_cyral_role.go similarity index 83% rename from cyral/data_source_cyral_role.go rename to cyral/internal/role/data_source_cyral_role.go index 38d8608c..5e38ff91 100644 --- a/cyral/data_source_cyral_role.go +++ b/cyral/internal/role/data_source_cyral_role.go @@ -1,14 +1,17 @@ -package cyral +package role import ( + "encoding/json" "fmt" + "log" "net/http" "regexp" "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" ) type GetUserGroupsResponse struct { @@ -76,21 +79,21 @@ type UserGroup struct { Mappings []*SSOGroup `json:"mappings"` } -func dataSourceRoleReadConfig() ResourceOperationConfig { - return ResourceOperationConfig{ +func dataSourceRoleReadConfig() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "RoleDataSourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/users/groups", c.ControlPlane) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &GetUserGroupsResponse{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &GetUserGroupsResponse{} }, } } -func dataSourceRole() *schema.Resource { +func DataSourceRole() *schema.Resource { return &schema.Resource{ Description: "Retrieve and filter [roles](https://cyral.com/docs/account-administration/acct-manage-cyral-roles/) that exist in the Cyral Control Plane.", - ReadContext: ReadResource(dataSourceRoleReadConfig()), + ReadContext: core.ReadResource(dataSourceRoleReadConfig()), Schema: map[string]*schema.Schema{ "name": { Description: "Filter the results by a regular expression (regex) that matches names of existing roles.", @@ -169,3 +172,21 @@ func dataSourceRole() *schema.Resource { }, } } + +func ListRoles(c *client.Client) (*GetUserGroupsResponse, error) { + log.Printf("[DEBUG] Init listRoles") + + url := fmt.Sprintf("https://%s/v1/users/groups", c.ControlPlane) + body, err := c.DoRequest(url, http.MethodGet, nil) + if err != nil { + return nil, err + } + resp := &GetUserGroupsResponse{} + if err := json.Unmarshal(body, resp); err != nil { + return nil, err + } + log.Printf("[DEBUG] Response body (unmarshalled): %#v", resp) + log.Printf("[DEBUG] End listRoles") + + return resp, nil +} diff --git a/cyral/data_source_cyral_role_test.go b/cyral/internal/role/data_source_cyral_role_test.go similarity index 55% rename from cyral/data_source_cyral_role_test.go rename to cyral/internal/role/data_source_cyral_role_test.go index 13192ffd..5d6da4be 100644 --- a/cyral/data_source_cyral_role_test.go +++ b/cyral/internal/role/data_source_cyral_role_test.go @@ -1,17 +1,24 @@ -package cyral +package role_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // TODO: More tests -aholmquist 2022-08-29 func TestAccRoleDataSource(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: roleDataSourceConfig( @@ -28,5 +35,5 @@ func roleDataSourceConfig(dsourceName, nameFilter string, dependsOn []string) st data "cyral_role" "%s" { name = "%s" depends_on = %s - }`, dsourceName, nameFilter, listToStr(dependsOn)) + }`, dsourceName, nameFilter, utils.ListToStr(dependsOn)) } diff --git a/cyral/model_permission.go b/cyral/internal/role/model_role.go similarity index 79% rename from cyral/model_permission.go rename to cyral/internal/role/model_role.go index 274f665d..f7280bef 100644 --- a/cyral/model_permission.go +++ b/cyral/internal/role/model_role.go @@ -1,42 +1,6 @@ -package cyral +package role -import ( - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -type Permission struct { - Id string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` -} - -func permissionsToInterfaceList(permissions []Permission) []any { - permissionsInterfaceList := make([]any, len(permissions)) - for index, permission := range permissions { - permissionsInterfaceList[index] = map[string]any{ - IDKey: permission.Id, - NameKey: permission.Name, - DescriptionKey: permission.Description, - } - } - return permissionsInterfaceList -} - -var allPermissionNames = []string{ - "Approval Management", - "Modify Policies", - "Modify Roles", - "Modify Sidecars and Repositories", - "Modify Users", - "Repo Crawler", - "View Audit Logs", - "View Datamaps", - "View Integrations", - "View Policies", - "View Roles", - "View Users", - "Modify Integrations", -} +import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" const ( // Schema keys diff --git a/cyral/resource_cyral_role.go b/cyral/internal/role/resource_cyral_role.go similarity index 82% rename from cyral/resource_cyral_role.go rename to cyral/internal/role/resource_cyral_role.go index 0671482f..dc0dc110 100644 --- a/cyral/resource_cyral_role.go +++ b/cyral/internal/role/resource_cyral_role.go @@ -1,4 +1,4 @@ -package cyral +package role import ( "context" @@ -8,7 +8,9 @@ import ( "net/http" "strings" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/permission" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -25,10 +27,10 @@ type RoleDataResponse struct { Id string `json:"id,omitempty"` Name string `json:"name,omitempty"` // Permissions correspond to Roles in API. - Permissions []*Permission `json:"roles,omitempty"` + Permissions []*permission.Permission `json:"roles,omitempty"` } -func resourceRole() *schema.Resource { +func ResourceRole() *schema.Resource { return &schema.Resource{ Description: "Manages [roles for Cyral control plane users](https://cyral.com/docs/account-administration/acct-manage-cyral-roles/#create-and-manage-administrator-roles-for-cyral-control-plane-users). See also: [Role SSO Groups](./role_sso_groups.md).", CreateContext: resourceRoleCreate, @@ -69,19 +71,19 @@ func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, m interface resourceData, err := getRoleDataFromResource(c, d) if err != nil { - return createError("Unable to create role", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to create role", fmt.Sprintf("%v", err)) } url := fmt.Sprintf("https://%s/v1/users/groups", c.ControlPlane) body, err := c.DoRequest(url, http.MethodPost, resourceData) if err != nil { - return createError("Unable to create role", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to create role", fmt.Sprintf("%v", err)) } response := RoleDataResponse{} if err := json.Unmarshal(body, &response); err != nil { - return createError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] Response body (unmarshalled): %#v", response) @@ -101,13 +103,13 @@ func resourceRoleRead(ctx context.Context, d *schema.ResourceData, m interface{} body, err := c.DoRequest(url, http.MethodGet, nil) if err != nil { - return createError(fmt.Sprintf("Unable to read role. Role Id: %s", + return utils.CreateError(fmt.Sprintf("Unable to read role. Role Id: %s", d.Id()), fmt.Sprintf("%v", err)) } response := RoleDataResponse{} if err := json.Unmarshal(body, &response); err != nil { - return createError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] Response body (unmarshalled): %#v", response) @@ -119,7 +121,7 @@ func resourceRoleRead(ctx context.Context, d *schema.ResourceData, m interface{} log.Printf("[DEBUG] resourceRoleRead - flatPermissions: %s", flatPermissions) if err := d.Set("permissions", flatPermissions); err != nil { - return createError(fmt.Sprintf("Unable to read role. Role Id: %s", + return utils.CreateError(fmt.Sprintf("Unable to read role. Role Id: %s", d.Id()), fmt.Sprintf("%v", err)) } } @@ -135,13 +137,13 @@ func resourceRoleUpdate(ctx context.Context, d *schema.ResourceData, m interface resourceData, err := getRoleDataFromResource(c, d) if err != nil { - return createError("Unable to update role", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to update role", fmt.Sprintf("%v", err)) } url := fmt.Sprintf("https://%s/v1/users/groups/%s", c.ControlPlane, d.Id()) if _, err := c.DoRequest(url, http.MethodPut, resourceData); err != nil { - return createError("Unable to update role", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to update role", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] End resourceRoleUpdate") @@ -156,7 +158,7 @@ func resourceRoleDelete(ctx context.Context, d *schema.ResourceData, m interface url := fmt.Sprintf("https://%s/v1/users/groups/%s", c.ControlPlane, d.Id()) if _, err := c.DoRequest(url, http.MethodDelete, nil); err != nil { - return createError("Unable to delete role", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to delete role", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] End resourceRoleDelete") @@ -191,7 +193,7 @@ func getRoleDataFromResource(c *client.Client, d *schema.ResourceData) (RoleData }, nil } -func flattenPermissions(permissions []*Permission) []interface{} { +func flattenPermissions(permissions []*permission.Permission) []interface{} { flatPermissions := make([]interface{}, 1) permissionsMap := make(map[string]interface{}) @@ -210,17 +212,17 @@ func formatPermissionName(permissionName string) string { return permissionName } -func getPermissionsFromAPI(c *client.Client) ([]*Permission, error) { +func getPermissionsFromAPI(c *client.Client) ([]*permission.Permission, error) { url := fmt.Sprintf("https://%s/v1/users/roles", c.ControlPlane) body, err := c.DoRequest(url, http.MethodGet, nil) if err != nil { - return []*Permission{}, err + return []*permission.Permission{}, err } response := RoleDataResponse{} if err := json.Unmarshal(body, &response); err != nil { - return []*Permission{}, err + return []*permission.Permission{}, err } return response.Permissions, nil diff --git a/cyral/resource_cyral_role_sso_groups.go b/cyral/internal/role/resource_cyral_role_sso_groups.go similarity index 84% rename from cyral/resource_cyral_role_sso_groups.go rename to cyral/internal/role/resource_cyral_role_sso_groups.go index d8d4d6ed..47c7b296 100644 --- a/cyral/resource_cyral_role_sso_groups.go +++ b/cyral/internal/role/resource_cyral_role_sso_groups.go @@ -1,4 +1,4 @@ -package cyral +package role import ( "context" @@ -6,7 +6,8 @@ import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -79,7 +80,7 @@ func roleSSOGroupsResourceSchemaV0() *schema.Resource { // Previously, the ID for cyral_role_sso_groups had the format // {role_id}/SSOGroups. The goal of this state upgrade is to remove the suffix // `SSOGroups`. -func upgradeRoleSSOGroupsV0( +func UpgradeRoleSSOGroupsV0( _ context.Context, rawState map[string]interface{}, _ interface{}, @@ -88,12 +89,12 @@ func upgradeRoleSSOGroupsV0( return rawState, nil } -func resourceRoleSSOGroups() *schema.Resource { +func ResourceRoleSSOGroups() *schema.Resource { return &schema.Resource{ Description: "Manages [mapping SSO groups to specific roles](https://cyral.com/docs/account-administration/acct-manage-cyral-roles/#map-an-sso-group-to-a-cyral-administrator-role) on Cyral control plane. See also: [Role](./role.md).", - CreateContext: CreateResource(createRoleSSOGroupsConfig, readRoleSSOGroupsConfig), - ReadContext: ReadResource(readRoleSSOGroupsConfig), - DeleteContext: DeleteResource(deleteRoleSSOGroupsConfig), + CreateContext: core.CreateResource(createRoleSSOGroupsConfig, readRoleSSOGroupsConfig), + ReadContext: core.ReadResource(readRoleSSOGroupsConfig), + DeleteContext: core.DeleteResource(deleteRoleSSOGroupsConfig), SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ @@ -101,7 +102,7 @@ func resourceRoleSSOGroups() *schema.Resource { Version: 0, Type: roleSSOGroupsResourceSchemaV0(). CoreConfigSchema().ImpliedType(), - Upgrade: upgradeRoleSSOGroupsV0, + Upgrade: UpgradeRoleSSOGroupsV0, }, }, @@ -120,36 +121,36 @@ func resourceRoleSSOGroups() *schema.Resource { } } -var createRoleSSOGroupsConfig = ResourceOperationConfig{ +var createRoleSSOGroupsConfig = core.ResourceOperationConfig{ Name: "resourceRoleSSOGroupsCreate", HttpMethod: http.MethodPatch, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/users/groups/%s/mappings", c.ControlPlane, d.Get("role_id").(string)) }, - NewResourceData: func() ResourceData { return &RoleSSOGroupsCreateRequest{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &RoleSSOGroupsCreateRequest{} }, + NewResourceData: func() core.ResourceData { return &RoleSSOGroupsCreateRequest{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &RoleSSOGroupsCreateRequest{} }, } -var readRoleSSOGroupsConfig = ResourceOperationConfig{ +var readRoleSSOGroupsConfig = core.ResourceOperationConfig{ Name: "resourceRoleSSOGroupsRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/users/groups/%s/mappings", c.ControlPlane, d.Get("role_id").(string)) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &RoleSSOGroupsReadResponse{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "Role SSO groups"}, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &RoleSSOGroupsReadResponse{} }, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Role SSO groups"}, } -var deleteRoleSSOGroupsConfig = ResourceOperationConfig{ +var deleteRoleSSOGroupsConfig = core.ResourceOperationConfig{ Name: "resourceRoleSSOGroupsDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/users/groups/%s/mappings", c.ControlPlane, d.Get("role_id").(string)) }, - NewResourceData: func() ResourceData { return &RoleSSOGroupsDeleteRequest{} }, + NewResourceData: func() core.ResourceData { return &RoleSSOGroupsDeleteRequest{} }, } func (data RoleSSOGroupsCreateRequest) WriteToSchema(d *schema.ResourceData) error { diff --git a/cyral/resource_cyral_role_sso_groups_test.go b/cyral/internal/role/resource_cyral_role_sso_groups_test.go similarity index 90% rename from cyral/resource_cyral_role_sso_groups_test.go rename to cyral/internal/role/resource_cyral_role_sso_groups_test.go index bbefe3bd..d71c214b 100644 --- a/cyral/resource_cyral_role_sso_groups_test.go +++ b/cyral/internal/role/resource_cyral_role_sso_groups_test.go @@ -1,4 +1,4 @@ -package cyral +package role_test import ( "context" @@ -6,7 +6,11 @@ import ( "regexp" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/role" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/require" ) @@ -20,7 +24,7 @@ const ( ) func roleSSOGroupsTestRoleName() string { - return accTestName(roleSSOGroupsResourceName, "role") + return utils.AccTestName(roleSSOGroupsResourceName, "role") } func roleSSOGroupsTestRole() string { @@ -31,10 +35,10 @@ func roleSSOGroupsTestRole() string { } func roleSSOGroupsTestOktaIntegration() string { - return formatBasicIntegrationIdPOktaIntoConfig( + return utils.FormatBasicIntegrationIdPOktaIntoConfig( testRoleSSOGroupsIntegrationResName, - accTestName(roleSSOGroupsResourceName, "integration"), - testSingleSignOnURL, + utils.AccTestName(roleSSOGroupsResourceName, "integration"), + utils.TestSingleSignOnURL, ) } @@ -44,7 +48,11 @@ func TestAccRoleSSOGroupsResource(t *testing.T) { importTestResourceName := "cyral_role_sso_groups.test_role_sso_groups" resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: testAccRoleSSOGroupsConfig_EmptyRoleId(), @@ -84,7 +92,7 @@ func TestRoleSSOGroupsResourceUpgradeV0(t *testing.T) { "id": "roleID/SSOGroups", "role_id": "roleID", } - actualNewState, err := upgradeRoleSSOGroupsV0(context.Background(), + actualNewState, err := role.UpgradeRoleSSOGroupsV0(context.Background(), previousState, nil) require.NoError(t, err) expectedNewState := map[string]interface{}{ diff --git a/cyral/resource_cyral_role_test.go b/cyral/internal/role/resource_cyral_role_test.go similarity index 93% rename from cyral/resource_cyral_role_test.go rename to cyral/internal/role/resource_cyral_role_test.go index 75930b74..f330c590 100644 --- a/cyral/resource_cyral_role_test.go +++ b/cyral/internal/role/resource_cyral_role_test.go @@ -1,23 +1,22 @@ -package cyral +package role_test import ( "fmt" "regexp" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -const ( - roleResourceName = "role" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func initialRoleName() string { - return accTestName(roleResourceName, "role") + return utils.AccTestName(utils.RoleResourceName, "role") } func updatedRoleName() string { - return accTestName(roleResourceName, "role-updated") + return utils.AccTestName(utils.RoleResourceName, "role-updated") } var onlyFalsePermissions = map[string]string{ @@ -63,7 +62,11 @@ func TestAccRoleResource(t *testing.T) { importResourceName := fmt.Sprintf("cyral_role.%s", updatedResName) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: testAccRoleConfig_EmptyRoleName(), diff --git a/cyral/data_source_cyral_saml_certificate.go b/cyral/internal/samlcertificate/data_source_cyral_saml_certificate.go similarity index 76% rename from cyral/data_source_cyral_saml_certificate.go rename to cyral/internal/samlcertificate/data_source_cyral_saml_certificate.go index 6e1ed711..b7bab240 100644 --- a/cyral/data_source_cyral_saml_certificate.go +++ b/cyral/internal/samlcertificate/data_source_cyral_saml_certificate.go @@ -1,25 +1,26 @@ -package cyral +package samlcertificate import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -func dataSourceSAMLCertificate() *schema.Resource { +func DataSourceSAMLCertificate() *schema.Resource { return &schema.Resource{ Description: "Retrieves a X.509 certificate used for signing SAML requests." + "\n\nSee also the remaining SAML-related resources and data sources.", - ReadContext: ReadResource(ResourceOperationConfig{ + ReadContext: core.ReadResource(core.ResourceOperationConfig{ Name: "dataSourceSAMLCertificateRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/integrations/saml/rsa/cert", c.ControlPlane) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &SAMLCertificateData{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &SAMLCertificateData{} }, }), Schema: map[string]*schema.Schema{ "id": { diff --git a/cyral/data_source_cyral_saml_certificate_test.go b/cyral/internal/samlcertificate/data_source_cyral_saml_certificate_test.go similarity index 66% rename from cyral/data_source_cyral_saml_certificate_test.go rename to cyral/internal/samlcertificate/data_source_cyral_saml_certificate_test.go index e3d28c49..0a22d28f 100644 --- a/cyral/data_source_cyral_saml_certificate_test.go +++ b/cyral/internal/samlcertificate/data_source_cyral_saml_certificate_test.go @@ -1,14 +1,20 @@ -package cyral +package samlcertificate_test import ( "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func TestAccSAMLCertificateDataSource(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: testAccSAMLCertificateConfig(), diff --git a/cyral/data_source_cyral_saml_configuration.go b/cyral/internal/samlconfiguration/data_source_cyral_saml_configuration.go similarity index 92% rename from cyral/data_source_cyral_saml_configuration.go rename to cyral/internal/samlconfiguration/data_source_cyral_saml_configuration.go index f831afb4..bff3ae95 100644 --- a/cyral/data_source_cyral_saml_configuration.go +++ b/cyral/internal/samlconfiguration/data_source_cyral_saml_configuration.go @@ -1,4 +1,4 @@ -package cyral +package samlconfiguration import ( "context" @@ -7,7 +7,9 @@ import ( "log" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/deprecated" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -17,7 +19,7 @@ var ( metadataTypes = []string{"base_64_saml_metadata_document", "saml_metadata_url"} ) -func dataSourceSAMLConfiguration() *schema.Resource { +func DataSourceSAMLConfiguration() *schema.Resource { return &schema.Resource{ Description: "Parses a SAML metadata URL or a Base64 document into a SAML configuration." + "\n\nSee also the remaining SAML-related resources and data sources.", @@ -177,12 +179,12 @@ func dataSourceSAMLConfigurationRead(ctx context.Context, d *schema.ResourceData body, err := c.DoRequest(url, http.MethodPost, metadataRequest) if err != nil { - return createError("Unable to retrieve saml configuration", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to retrieve saml configuration", fmt.Sprintf("%v", err)) } - response := SAMLConfiguration{} + response := deprecated.SAMLConfiguration{} if err := json.Unmarshal(body, &response); err != nil { - return createError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] Response body (unmarshalled): %#v", response) @@ -195,14 +197,14 @@ func dataSourceSAMLConfigurationRead(ctx context.Context, d *schema.ResourceData return diag.Diagnostics{} } -func getSAMLMetadataRequestFromSchema(d *schema.ResourceData) ParseSAMLMetadataRequest { - return ParseSAMLMetadataRequest{ +func getSAMLMetadataRequestFromSchema(d *schema.ResourceData) deprecated.ParseSAMLMetadataRequest { + return deprecated.ParseSAMLMetadataRequest{ SamlMetadataURL: d.Get("saml_metadata_url").(string), Base64SamlMetadataDocument: d.Get("base_64_saml_metadata_document").(string), } } -func setSAMLConfigurationToSchema(d *schema.ResourceData, data SAMLConfiguration) { +func setSAMLConfigurationToSchema(d *schema.ResourceData, data deprecated.SAMLConfiguration) { if data.Config != nil { d.Set("disable_using_jwks_url", data.Config.DisableUsingJWKSUrl) d.Set("sync_mode", data.Config.SyncMode) diff --git a/cyral/data_source_cyral_saml_configuration_test.go b/cyral/internal/samlconfiguration/data_source_cyral_saml_configuration_test.go similarity index 95% rename from cyral/data_source_cyral_saml_configuration_test.go rename to cyral/internal/samlconfiguration/data_source_cyral_saml_configuration_test.go index 26107dd3..1029d3ef 100644 --- a/cyral/data_source_cyral_saml_configuration_test.go +++ b/cyral/internal/samlconfiguration/data_source_cyral_saml_configuration_test.go @@ -1,11 +1,13 @@ -package cyral +package samlconfiguration_test import ( "fmt" "regexp" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const ( @@ -15,7 +17,11 @@ const ( func TestAccSAMLConfigurationDataSource(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: testAccSAMLConfigurationConfig_EmptyMetadata(), diff --git a/cyral/model_service_account.go b/cyral/internal/serviceaccount/model_service_account.go similarity index 60% rename from cyral/model_service_account.go rename to cyral/internal/serviceaccount/model_service_account.go index 93cdd05f..d653f087 100644 --- a/cyral/model_service_account.go +++ b/cyral/internal/serviceaccount/model_service_account.go @@ -1,8 +1,9 @@ -package cyral +package serviceaccount import ( "fmt" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -15,9 +16,9 @@ type ServiceAccount struct { } func (serviceAccount *ServiceAccount) ReadFromSchema(d *schema.ResourceData) error { - serviceAccount.DisplayName = d.Get(serviceAccountResourceDisplayNameKey).(string) - permissionIDs := convertFromInterfaceList[string]( - d.Get(serviceAccountResourcePermissionIDsKey).(*schema.Set).List(), + serviceAccount.DisplayName = d.Get(ServiceAccountResourceDisplayNameKey).(string) + permissionIDs := utils.ConvertFromInterfaceList[string]( + d.Get(ServiceAccountResourcePermissionIDsKey).(*schema.Set).List(), ) if len(permissionIDs) == 0 { return fmt.Errorf("at least one permission must be specified for the service account") @@ -28,12 +29,12 @@ func (serviceAccount *ServiceAccount) ReadFromSchema(d *schema.ResourceData) err func (serviceAccount *ServiceAccount) WriteToSchema(d *schema.ResourceData) error { d.SetId(serviceAccount.ClientID) - d.Set(serviceAccountResourceDisplayNameKey, serviceAccount.DisplayName) - d.Set(serviceAccountResourceClientIDKey, serviceAccount.ClientID) + d.Set(ServiceAccountResourceDisplayNameKey, serviceAccount.DisplayName) + d.Set(ServiceAccountResourceClientIDKey, serviceAccount.ClientID) isCreateResponse := serviceAccount.ClientSecret != "" if isCreateResponse { - d.Set(serviceAccountResourceClientSecretKey, serviceAccount.ClientSecret) + d.Set(ServiceAccountResourceClientSecretKey, serviceAccount.ClientSecret) } - d.Set(serviceAccountResourcePermissionIDsKey, convertToInterfaceList(serviceAccount.PermissionIDs)) + d.Set(ServiceAccountResourcePermissionIDsKey, utils.ConvertToInterfaceList(serviceAccount.PermissionIDs)) return nil } diff --git a/cyral/resource_cyral_service_account.go b/cyral/internal/serviceaccount/resource_cyral_service_account.go similarity index 68% rename from cyral/resource_cyral_service_account.go rename to cyral/internal/serviceaccount/resource_cyral_service_account.go index 911362ef..5cf79968 100644 --- a/cyral/resource_cyral_service_account.go +++ b/cyral/internal/serviceaccount/resource_cyral_service_account.go @@ -1,23 +1,25 @@ -package cyral +package serviceaccount import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const ( // Schema keys - serviceAccountResourceDisplayNameKey = "display_name" - serviceAccountResourcePermissionIDsKey = "permission_ids" - serviceAccountResourceClientIDKey = "client_id" - serviceAccountResourceClientSecretKey = "client_secret" + ServiceAccountResourceDisplayNameKey = "display_name" + ServiceAccountResourcePermissionIDsKey = "permission_ids" + ServiceAccountResourceClientIDKey = "client_id" + ServiceAccountResourceClientSecretKey = "client_secret" ) var ( - ReadServiceAccountConfig = ResourceOperationConfig{ + ReadServiceAccountConfig = core.ResourceOperationConfig{ Name: "ServiceAccountRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -27,22 +29,22 @@ var ( d.Id(), ) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &ServiceAccount{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "Service account"}, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Service account"}, } ) -func resourceServiceAccount() *schema.Resource { +func ResourceServiceAccount() *schema.Resource { return &schema.Resource{ Description: "Manages a Cyral Service Account (A.k.a: " + "[Cyral API Access Key](https://cyral.com/docs/api-ref/api-intro/#api-access-key)). See also " + "data source [`cyral_permission`](../data-sources/permission.md)." + "\n\n-> **Note** This resource does not support importing, since the client secret cannot " + "be read after the resource creation.", - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "ServiceAccountCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -51,18 +53,18 @@ func resourceServiceAccount() *schema.Resource { c.ControlPlane, ) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return &ServiceAccount{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &ServiceAccount{} }, }, ReadServiceAccountConfig, ), - ReadContext: ReadResource(ReadServiceAccountConfig), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadServiceAccountConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "ServiceAccountUpdate", HttpMethod: http.MethodPatch, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -72,14 +74,14 @@ func resourceServiceAccount() *schema.Resource { d.Id(), ) }, - NewResourceData: func() ResourceData { + NewResourceData: func() core.ResourceData { return &ServiceAccount{} }, }, ReadServiceAccountConfig, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "ServiceAccountDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { @@ -93,12 +95,12 @@ func resourceServiceAccount() *schema.Resource { ), Schema: map[string]*schema.Schema{ - serviceAccountResourceDisplayNameKey: { + ServiceAccountResourceDisplayNameKey: { Description: "The service account display name.", Type: schema.TypeString, Required: true, }, - serviceAccountResourcePermissionIDsKey: { + ServiceAccountResourcePermissionIDsKey: { Description: "A list of permission IDs that will be assigned to this service account. See " + "also data source [`cyral_permission`](../data-sources/permission.md).", Type: schema.TypeSet, @@ -107,20 +109,20 @@ func resourceServiceAccount() *schema.Resource { Type: schema.TypeString, }, }, - IDKey: { + utils.IDKey: { Description: fmt.Sprintf( "The resource identifier. It's equal to `%s`.", - serviceAccountResourceClientIDKey, + ServiceAccountResourceClientIDKey, ), Type: schema.TypeString, Computed: true, }, - serviceAccountResourceClientIDKey: { + ServiceAccountResourceClientIDKey: { Description: "The service account client ID.", Type: schema.TypeString, Computed: true, }, - serviceAccountResourceClientSecretKey: { + ServiceAccountResourceClientSecretKey: { Description: "The service account client secret. **Note**: This resource is not able to recognize " + "changes to the client secret after its creation, so keep in mind that if the client secret is " + "rotated, the value present in this attribute will be outdated. If you need to rotate the client " + diff --git a/cyral/resource_cyral_service_account_test.go b/cyral/internal/serviceaccount/resource_cyral_service_account_test.go similarity index 75% rename from cyral/resource_cyral_service_account_test.go rename to cyral/internal/serviceaccount/resource_cyral_service_account_test.go index 3e2eec28..94a08a4b 100644 --- a/cyral/resource_cyral_service_account_test.go +++ b/cyral/internal/serviceaccount/resource_cyral_service_account_test.go @@ -1,10 +1,14 @@ -package cyral +package serviceaccount_test import ( "fmt" "regexp" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/permission" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/serviceaccount" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -25,7 +29,7 @@ func TestAccServiceAccountResource(t *testing.T) { ) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: testSteps, }) } @@ -40,7 +44,7 @@ func accTestStepServiceAccountResource_RequiredArgumentDisplayName(resourceName ExpectError: regexp.MustCompile( fmt.Sprintf( `The argument "%s" is required, but no definition was found.`, - serviceAccountResourceDisplayNameKey, + serviceaccount.ServiceAccountResourceDisplayNameKey, ), ), } @@ -58,7 +62,7 @@ func accTestStepServiceAccountResource_RequiredArgumentPermissions(resourceName ExpectError: regexp.MustCompile( fmt.Sprintf( `The argument "%s" is required, but no definition was found.`, - serviceAccountResourcePermissionIDsKey, + serviceaccount.ServiceAccountResourcePermissionIDsKey, ), ), } @@ -80,7 +84,7 @@ func accTestStepServiceAccountResource_EmptyPermissions(resourceName string) res } func accTestStepServiceAccountResource_SinglePermission(resourceName string) resource.TestStep { - displayName := accTestName("service-account", "service-account-1") + displayName := utils.AccTestName("service-account", "service-account-1") permissionNames := []string{"Modify Policies"} config, check := getAccTestStepForServiceAccountResourceFullConfig( resourceName, @@ -94,7 +98,7 @@ func accTestStepServiceAccountResource_SinglePermission(resourceName string) res } func accTestStepServiceAccountResource_DuplicatedPermission(resourceName string) resource.TestStep { - displayName := accTestName("service-account", "service-account-1") + displayName := utils.AccTestName("service-account", "service-account-1") permissionNames := []string{"Modify Policies", "Modify Policies"} config, _ := getAccTestStepForServiceAccountResourceFullConfig( resourceName, @@ -105,27 +109,27 @@ func accTestStepServiceAccountResource_DuplicatedPermission(resourceName string) check := resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( resourceFullName, - serviceAccountResourceDisplayNameKey, + serviceaccount.ServiceAccountResourceDisplayNameKey, displayName, ), resource.TestCheckResourceAttr( resourceFullName, - fmt.Sprintf("%s.#", serviceAccountResourcePermissionIDsKey), + fmt.Sprintf("%s.#", serviceaccount.ServiceAccountResourcePermissionIDsKey), "1", ), resource.TestCheckResourceAttrPair( resourceFullName, - IDKey, + utils.IDKey, resourceFullName, - serviceAccountResourceClientIDKey, + serviceaccount.ServiceAccountResourceClientIDKey, ), resource.TestCheckResourceAttrSet( resourceFullName, - serviceAccountResourceClientIDKey, + serviceaccount.ServiceAccountResourceClientIDKey, ), resource.TestCheckResourceAttrSet( resourceFullName, - serviceAccountResourceClientSecretKey, + serviceaccount.ServiceAccountResourceClientSecretKey, ), ) return resource.TestStep{ @@ -135,11 +139,11 @@ func accTestStepServiceAccountResource_DuplicatedPermission(resourceName string) } func accTestStepServiceAccountResource_AllPermissions(resourceName string) resource.TestStep { - displayName := accTestName("service-account", "service-account-1") + displayName := utils.AccTestName("service-account", "service-account-1") config, check := getAccTestStepForServiceAccountResourceFullConfig( resourceName, displayName, - allPermissionNames, + permission.AllPermissionNames, ) return resource.TestStep{ Config: config, @@ -148,7 +152,7 @@ func accTestStepServiceAccountResource_AllPermissions(resourceName string) resou } func accTestStepServiceAccountResource_UpdatedFields(resourceName string) resource.TestStep { - displayName := accTestName("service-account", "service-account-1-updated") + displayName := utils.AccTestName("service-account", "service-account-1-updated") permissionNames := []string{ "Approval Management", "Modify Roles", @@ -174,12 +178,12 @@ func getAccTestStepForServiceAccountResourceFullConfig( displayName string, permissionNames []string, ) (string, resource.TestCheckFunc) { - config := formatBasicDataSourcePermissionIntoConfig("permissions") + config := utils.FormatBasicDataSourcePermissionIntoConfig("permissions") config += fmt.Sprintf(` locals { serviceAccountPermissions = %s } - `, listToStr(permissionNames), + `, utils.ListToStr(permissionNames), ) config += fmt.Sprintf(` resource "cyral_service_account" "%s" { @@ -189,33 +193,33 @@ func getAccTestStepForServiceAccountResourceFullConfig( if contains(local.serviceAccountPermissions, permission.name) ] } - `, resourceName, displayName, PermissionDataSourcePermissionListKey, + `, resourceName, displayName, permission.PermissionDataSourcePermissionListKey, ) resourceFullName := fmt.Sprintf("cyral_service_account.%s", resourceName) checks := []resource.TestCheckFunc{ resource.TestCheckResourceAttr( resourceFullName, - serviceAccountResourceDisplayNameKey, + serviceaccount.ServiceAccountResourceDisplayNameKey, displayName, ), resource.TestCheckResourceAttr( resourceFullName, - fmt.Sprintf("%s.#", serviceAccountResourcePermissionIDsKey), + fmt.Sprintf("%s.#", serviceaccount.ServiceAccountResourcePermissionIDsKey), fmt.Sprintf("%d", len(permissionNames)), ), resource.TestCheckResourceAttrPair( resourceFullName, - IDKey, + utils.IDKey, resourceFullName, - serviceAccountResourceClientIDKey, + serviceaccount.ServiceAccountResourceClientIDKey, ), resource.TestCheckResourceAttrSet( resourceFullName, - serviceAccountResourceClientIDKey, + serviceaccount.ServiceAccountResourceClientIDKey, ), resource.TestCheckResourceAttrSet( resourceFullName, - serviceAccountResourceClientSecretKey, + serviceaccount.ServiceAccountResourceClientSecretKey, ), } return config, resource.ComposeTestCheckFunc(checks...) diff --git a/cyral/resource_cyral_sidecar_credentials.go b/cyral/internal/sidecar/credentials/resource_cyral_sidecar_credentials.go similarity index 84% rename from cyral/resource_cyral_sidecar_credentials.go rename to cyral/internal/sidecar/credentials/resource_cyral_sidecar_credentials.go index 4fcf3ca5..3d398d01 100644 --- a/cyral/resource_cyral_sidecar_credentials.go +++ b/cyral/internal/sidecar/credentials/resource_cyral_sidecar_credentials.go @@ -1,4 +1,4 @@ -package cyral +package credentials import ( "context" @@ -7,7 +7,8 @@ import ( "log" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -22,7 +23,7 @@ type SidecarCredentialsData struct { ClientSecret string `json:"clientSecret"` } -func resourceSidecarCredentials() *schema.Resource { +func ResourceSidecarCredentials() *schema.Resource { return &schema.Resource{ Description: "Create new [credentials for Cyral sidecar](https://cyral.com/docs/sidecars/sidecar-manage/#rotate-the-client-secret-for-a-sidecar).", CreateContext: resourceSidecarCredentialsCreate, @@ -69,12 +70,12 @@ func resourceSidecarCredentialsCreate(ctx context.Context, d *schema.ResourceDat body, err := c.DoRequest(url, http.MethodPost, payload) if err != nil { - return createError("Unable to create sidecar credentials", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to create sidecar credentials", fmt.Sprintf("%v", err)) } response := SidecarCredentialsData{} if err := json.Unmarshal(body, &response); err != nil { - return createError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] Response body (unmarshalled): %#v", response) @@ -93,13 +94,13 @@ func resourceSidecarCredentialsRead(ctx context.Context, d *schema.ResourceData, body, err := c.DoRequest(url, http.MethodGet, nil) if err != nil { - return createError(fmt.Sprintf("Unable to read sidecar credentials. ClientID: %s", + return utils.CreateError(fmt.Sprintf("Unable to read sidecar credentials. ClientID: %s", d.Id()), fmt.Sprintf("%v", err)) } response := SidecarCredentialsData{} if err := json.Unmarshal(body, &response); err != nil { - return createError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] Response body (unmarshalled): %#v", response) @@ -118,7 +119,7 @@ func resourceSidecarCredentialsDelete(ctx context.Context, d *schema.ResourceDat url := fmt.Sprintf("https://%s/v1/users/sidecarAccounts/%s", c.ControlPlane, d.Id()) if _, err := c.DoRequest(url, http.MethodDelete, nil); err != nil { - return createError("Unable to delete sidecar credentials", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to delete sidecar credentials", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] End resourceSidecarCredentialsDelete") diff --git a/cyral/resource_cyral_sidecar_credentials_test.go b/cyral/internal/sidecar/credentials/resource_cyral_sidecar_credentials_test.go similarity index 81% rename from cyral/resource_cyral_sidecar_credentials_test.go rename to cyral/internal/sidecar/credentials/resource_cyral_sidecar_credentials_test.go index ced69221..3527ccc4 100644 --- a/cyral/resource_cyral_sidecar_credentials_test.go +++ b/cyral/internal/sidecar/credentials/resource_cyral_sidecar_credentials_test.go @@ -1,9 +1,11 @@ -package cyral +package credentials_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -16,7 +18,7 @@ func TestAccSidecarCredentialsResource(t *testing.T) { resource.ParallelTest( t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -60,16 +62,16 @@ func setupSidecarCredentialsTest() (string, resource.TestCheckFunc) { func createSidecarCredentialsConfig() string { var config string - config += formatBasicSidecarIntoConfig( - basicSidecarResName, - accTestName(sidecarCredentialsResourceName, "sidecar"), + config += utils.FormatBasicSidecarIntoConfig( + utils.BasicSidecarResName, + utils.AccTestName(sidecarCredentialsResourceName, "sidecar"), "docker", "", ) config += fmt.Sprintf( ` resource "cyral_sidecar_credentials" "test_sidecar_credentials" { sidecar_id = %s - }`, basicSidecarID, + }`, utils.BasicSidecarID, ) return config } diff --git a/cyral/data_source_cyral_sidecar_bound_ports.go b/cyral/internal/sidecar/data_source_cyral_sidecar_bound_ports.go similarity index 88% rename from cyral/data_source_cyral_sidecar_bound_ports.go rename to cyral/internal/sidecar/data_source_cyral_sidecar_bound_ports.go index 2dc681c8..a3a350a7 100644 --- a/cyral/data_source_cyral_sidecar_bound_ports.go +++ b/cyral/internal/sidecar/data_source_cyral_sidecar_bound_ports.go @@ -1,4 +1,4 @@ -package cyral +package sidecar import ( "context" @@ -8,7 +8,9 @@ import ( "net/http" "sort" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/listener" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -29,10 +31,10 @@ type BindingComponent struct { } type ListenerComponent struct { - Address *NetworkAddress `json:"address,omitempty"` + Address *listener.NetworkAddress `json:"address,omitempty"` } -func dataSourceSidecarBoundPorts() *schema.Resource { +func DataSourceSidecarBoundPorts() *schema.Resource { return &schema.Resource{ Description: "Retrieves all the ports of a given sidecar that are currently bound to repositories.", ReadContext: dataSourceSidecarBoundPortsRead, @@ -72,7 +74,7 @@ func dataSourceSidecarBoundPortsRead( sidecarID := d.Get("sidecar_id").(string) composedBindings, err := getComposedBindings(c, sidecarID) if err != nil { - return createError(fmt.Sprintf("Unable to retrieve repo IDs bound to sidecar. SidecarID: %s", + return utils.CreateError(fmt.Sprintf("Unable to retrieve repo IDs bound to sidecar. SidecarID: %s", sidecarID), err.Error()) } diff --git a/cyral/data_source_cyral_sidecar_bound_ports_test.go b/cyral/internal/sidecar/data_source_cyral_sidecar_bound_ports_test.go similarity index 69% rename from cyral/data_source_cyral_sidecar_bound_ports_test.go rename to cyral/internal/sidecar/data_source_cyral_sidecar_bound_ports_test.go index c9345b5f..3cb91239 100644 --- a/cyral/data_source_cyral_sidecar_bound_ports_test.go +++ b/cyral/internal/sidecar/data_source_cyral_sidecar_bound_ports_test.go @@ -1,11 +1,14 @@ -package cyral +package sidecar_test import ( "fmt" "regexp" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const ( @@ -13,9 +16,9 @@ const ( ) func dsourceSidecarBoundPortsSampleSidecarConfig() string { - return formatBasicSidecarIntoConfig( - basicSidecarResName, - accTestName(sidecarBoundPortsDataSourceName, "sidecar"), + return utils.FormatBasicSidecarIntoConfig( + utils.BasicSidecarResName, + utils.AccTestName(sidecarBoundPortsDataSourceName, "sidecar"), "cft-ec2", "", ) } @@ -23,7 +26,11 @@ func dsourceSidecarBoundPortsSampleSidecarConfig() string { func TestAccSidecarBoundPortsDataSource(t *testing.T) { resource.ParallelTest( t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: testAccSidecarBoundPortsConfig_EmptySidecarID(), @@ -56,7 +63,7 @@ func testAccSidecarBoundPortsConfig_NoBindings() string { ` data "cyral_sidecar_bound_ports" "sidecar_bound_ports_1" { sidecar_id = %s - }`, basicSidecarID, + }`, utils.BasicSidecarID, ) return config } @@ -75,82 +82,82 @@ func testAccSidecarBoundPortsConfig_MultipleBindings() string { config += dsourceSidecarBoundPortsSampleSidecarConfig() // Repo 1 - config += formatBasicRepositoryIntoConfig( + config += utils.FormatBasicRepositoryIntoConfig( "repo_1", - accTestName(sidecarBoundPortsDataSourceName, "repo1"), + utils.AccTestName(sidecarBoundPortsDataSourceName, "repo1"), "mysql", "mysql.com", 3306, ) - config += formatBasicSidecarListenerIntoConfig( + config += utils.FormatBasicSidecarListenerIntoConfig( "listener_1", - basicSidecarID, + utils.BasicSidecarID, "mysql", 3306, ) - config += formatBasicRepositoryBindingIntoConfig( + config += utils.FormatBasicRepositoryBindingIntoConfig( "binding_1", - basicSidecarID, + utils.BasicSidecarID, "cyral_repository.repo_1.id", "cyral_sidecar_listener.listener_1.listener_id", ) // Repo 2 - config += formatBasicRepositoryIntoConfig( + config += utils.FormatBasicRepositoryIntoConfig( "repo_2", - accTestName(sidecarBoundPortsDataSourceName, "repo2"), + utils.AccTestName(sidecarBoundPortsDataSourceName, "repo2"), "mongodb", "mongo.com", 27017, ) - config += formatBasicSidecarListenerIntoConfig( + config += utils.FormatBasicSidecarListenerIntoConfig( "listener_2", - basicSidecarID, + utils.BasicSidecarID, "mongodb", 27017, ) - config += formatBasicRepositoryBindingIntoConfig( + config += utils.FormatBasicRepositoryBindingIntoConfig( "binding_2", - basicSidecarID, + utils.BasicSidecarID, "cyral_repository.repo_2.id", "cyral_sidecar_listener.listener_2.listener_id", ) // Repo 3 - config += formatBasicRepositoryIntoConfig( + config += utils.FormatBasicRepositoryIntoConfig( "repo_3", - accTestName(sidecarBoundPortsDataSourceName, "repo3"), + utils.AccTestName(sidecarBoundPortsDataSourceName, "repo3"), "oracle", "oracle.com", 1234, ) - config += formatBasicSidecarListenerIntoConfig( + config += utils.FormatBasicSidecarListenerIntoConfig( "listener_3", - basicSidecarID, + utils.BasicSidecarID, "oracle", 1234, ) - config += formatBasicRepositoryBindingIntoConfig( + config += utils.FormatBasicRepositoryBindingIntoConfig( "binding_3", - basicSidecarID, + utils.BasicSidecarID, "cyral_repository.repo_3.id", "cyral_sidecar_listener.listener_3.listener_id", ) // Repo 4 - config += formatBasicRepositoryIntoConfig( + config += utils.FormatBasicRepositoryIntoConfig( "repo_4", - accTestName(sidecarBoundPortsDataSourceName, "repo4"), + utils.AccTestName(sidecarBoundPortsDataSourceName, "repo4"), "s3", "s3.com", 5678, ) - config += formatBasicSidecarListenerIntoConfig( + config += utils.FormatBasicSidecarListenerIntoConfig( "listener_4", - basicSidecarID, + utils.BasicSidecarID, "s3", 5678, ) - config += formatBasicRepositoryBindingIntoConfig( + config += utils.FormatBasicRepositoryBindingIntoConfig( "binding_4", - basicSidecarID, + utils.BasicSidecarID, "cyral_repository.repo_4.id", "cyral_sidecar_listener.listener_4.listener_id", ) @@ -168,7 +175,7 @@ func testAccSidecarBoundPortsConfig_MultipleBindings() string { cyral_repository_binding.binding_4 ] sidecar_id = %s - }`, basicSidecarID, + }`, utils.BasicSidecarID, ) return config diff --git a/cyral/data_source_cyral_sidecar_id.go b/cyral/internal/sidecar/data_source_cyral_sidecar_id.go similarity index 58% rename from cyral/data_source_cyral_sidecar_id.go rename to cyral/internal/sidecar/data_source_cyral_sidecar_id.go index e7e078ba..1a7cfaae 100644 --- a/cyral/data_source_cyral_sidecar_id.go +++ b/cyral/internal/sidecar/data_source_cyral_sidecar_id.go @@ -1,11 +1,14 @@ -package cyral +package sidecar import ( "context" + "encoding/json" "fmt" "log" + "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -16,7 +19,7 @@ type IdentifiedSidecarInfo struct { Sidecar SidecarData `json:"sidecar"` } -func dataSourceSidecarID() *schema.Resource { +func DataSourceSidecarID() *schema.Resource { return &schema.Resource{ Description: "Given a sidecar name, retrieves the respective sidecar ID.", ReadContext: dataSourceSidecarIDRead, @@ -43,9 +46,9 @@ func dataSourceSidecarIDRead( log.Printf("[DEBUG] Init dataSourceSidecarIDRead") c := m.(*client.Client) - sidecarsInfo, err := listSidecars(c) + sidecarsInfo, err := ListSidecars(c) if err != nil { - return createError("Unable to retrieve the list of existent sidecars.", err.Error()) + return utils.CreateError("Unable to retrieve the list of existent sidecars.", err.Error()) } sidecarName := d.Get("sidecar_name").(string) @@ -57,7 +60,7 @@ func dataSourceSidecarIDRead( } if d.Id() == "" { - return createError("Sidecar not found.", + return utils.CreateError("Sidecar not found.", fmt.Sprintf("No sidecar found for name '%s'.", sidecarName)) } @@ -66,3 +69,21 @@ func dataSourceSidecarIDRead( return diag.Diagnostics{} } + +func ListSidecars(c *client.Client) ([]IdentifiedSidecarInfo, error) { + log.Printf("[DEBUG] Init listSidecars") + url := fmt.Sprintf("https://%s/v1/sidecars", c.ControlPlane) + body, err := c.DoRequest(url, http.MethodGet, nil) + if err != nil { + return nil, err + } + + var sidecarsInfo []IdentifiedSidecarInfo + if err := json.Unmarshal(body, &sidecarsInfo); err != nil { + return nil, err + } + log.Printf("[DEBUG] Response body (unmarshalled): %#v", sidecarsInfo) + log.Printf("[DEBUG] End listSidecars") + + return sidecarsInfo, nil +} diff --git a/cyral/data_source_cyral_sidecar_id_test.go b/cyral/internal/sidecar/data_source_cyral_sidecar_id_test.go similarity index 73% rename from cyral/data_source_cyral_sidecar_id_test.go rename to cyral/internal/sidecar/data_source_cyral_sidecar_id_test.go index 3416b864..3c065581 100644 --- a/cyral/data_source_cyral_sidecar_id_test.go +++ b/cyral/internal/sidecar/data_source_cyral_sidecar_id_test.go @@ -1,11 +1,14 @@ -package cyral +package sidecar_test import ( "fmt" "regexp" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const ( @@ -17,7 +20,11 @@ func TestAccSidecarIDDataSource(t *testing.T) { resource.ParallelTest( t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: testAccSidecarIDConfig_EmptySidecarName(), @@ -60,16 +67,16 @@ func testAccSidecarIDConfig_NoSidecarFoundForGivenName(nonExistentSidecarName st func testAccSidecarIDConfig_ExistentSidecar() string { var config string - config += formatBasicSidecarIntoConfig( - basicSidecarResName, - accTestName(sidecarIDDataSourceName, "sidecar"), + config += utils.FormatBasicSidecarIntoConfig( + utils.BasicSidecarResName, + utils.AccTestName(sidecarIDDataSourceName, "sidecar"), "cft-ec2", "", ) config += fmt.Sprintf( ` data "cyral_sidecar_id" "sidecar_id" { sidecar_name = cyral_sidecar.%s.name - }`, basicSidecarResName, + }`, utils.BasicSidecarResName, ) return config } @@ -77,7 +84,7 @@ func testAccSidecarIDConfig_ExistentSidecar() string { func testAccSidecarIDCheck_ExistentSidecar() resource.TestCheckFunc { return resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair( - fmt.Sprintf("cyral_sidecar.%s", basicSidecarResName), "id", + fmt.Sprintf("cyral_sidecar.%s", utils.BasicSidecarResName), "id", "data.cyral_sidecar_id.sidecar_id", "id", ), ) diff --git a/cyral/data_source_cyral_sidecar_health.go b/cyral/internal/sidecar/health/data_source_cyral_sidecar_health.go similarity index 71% rename from cyral/data_source_cyral_sidecar_health.go rename to cyral/internal/sidecar/health/data_source_cyral_sidecar_health.go index 7012e488..3cbe41c9 100644 --- a/cyral/data_source_cyral_sidecar_health.go +++ b/cyral/internal/sidecar/health/data_source_cyral_sidecar_health.go @@ -1,59 +1,56 @@ -package cyral +package health import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -const ( - // Schema keys - StatusKey = "status" -) - type SidecarHealth struct { Status string `json:"status"` } func (health *SidecarHealth) WriteToSchema(d *schema.ResourceData) error { d.SetId(uuid.New().String()) - d.Set(StatusKey, health.Status) + d.Set(utils.StatusKey, health.Status) return nil } -func dataSourceSidecarHealth() *schema.Resource { +func DataSourceSidecarHealth() *schema.Resource { return &schema.Resource{ Description: "Retrieve aggregated information about the " + "[sidecar's health](https://cyral.com/docs/sidecars/sidecar-manage/#check-sidecar-cluster-status), " + "considering all instances of the sidecar.", - ReadContext: ReadResource(ResourceOperationConfig{ + ReadContext: core.ReadResource(core.ResourceOperationConfig{ Name: "SidecarHealthDataSourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf( - "https://%s/v2/sidecars/%s/health", c.ControlPlane, d.Get(SidecarIDKey), + "https://%s/v2/sidecars/%s/health", c.ControlPlane, d.Get(utils.SidecarIDKey), ) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &SidecarHealth{} }, }), Schema: map[string]*schema.Schema{ - SidecarIDKey: { + utils.SidecarIDKey: { Description: "ID of the Sidecar that will be used to retrieve health information.", Type: schema.TypeString, Required: true, }, - IDKey: { + utils.IDKey: { Description: "Data source identifier.", Type: schema.TypeString, Computed: true, }, - StatusKey: { + utils.StatusKey: { Description: "Sidecar health status. Possible values are: `HEALTHY`, `DEGRADED`, `UNHEALTHY` " + "and `UNKNOWN`. For more information, see " + "[Sidecar Status](https://cyral.com/docs/sidecars/sidecar-manage/#check-sidecar-cluster-status).", diff --git a/cyral/data_source_cyral_sidecar_health_test.go b/cyral/internal/sidecar/health/data_source_cyral_sidecar_health_test.go similarity index 77% rename from cyral/data_source_cyral_sidecar_health_test.go rename to cyral/internal/sidecar/health/data_source_cyral_sidecar_health_test.go index 14d980cf..ed4ef88e 100644 --- a/cyral/data_source_cyral_sidecar_health_test.go +++ b/cyral/internal/sidecar/health/data_source_cyral_sidecar_health_test.go @@ -1,10 +1,12 @@ -package cyral +package health_test import ( "fmt" "regexp" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -19,7 +21,7 @@ func TestAccSidecarHealthDataSource(t *testing.T) { accTestStepSidecarHealthDataSource_ListAllSidecarHealthInfo(dataSourceName), } resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: testSteps, }) } @@ -34,16 +36,16 @@ func accTestStepSidecarHealthDataSource_RequiredArgumentSidecarID(dataSourceName ExpectError: regexp.MustCompile( fmt.Sprintf( `The argument "%s" is required, but no definition was found.`, - SidecarIDKey, + utils.SidecarIDKey, ), ), } } func accTestStepSidecarHealthDataSource_ListAllSidecarHealthInfo(dataSourceName string) resource.TestStep { - config := formatBasicSidecarIntoConfig( - basicSidecarResName, - accTestName("data-sidecar-health", "sidecar"), + config := utils.FormatBasicSidecarIntoConfig( + utils.BasicSidecarResName, + utils.AccTestName("data-sidecar-health", "sidecar"), "cft-ec2", "", ) @@ -51,16 +53,16 @@ func accTestStepSidecarHealthDataSource_ListAllSidecarHealthInfo(dataSourceName data "cyral_sidecar_health" "%s" { sidecar_id = %s } - `, dataSourceName, basicSidecarID) + `, dataSourceName, utils.BasicSidecarID) dataSourceFullName := fmt.Sprintf(sidecarHealthDataSourceFullNameFmt, dataSourceName) check := resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet( dataSourceFullName, - IDKey, + utils.IDKey, ), resource.TestCheckResourceAttr( dataSourceFullName, - StatusKey, + utils.StatusKey, "UNKNOWN", ), ) diff --git a/cyral/data_source_cyral_sidecar_instance.go b/cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance.go similarity index 90% rename from cyral/data_source_cyral_sidecar_instance.go rename to cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance.go index 918f449c..d3a3fba3 100644 --- a/cyral/data_source_cyral_sidecar_instance.go +++ b/cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance.go @@ -1,10 +1,12 @@ -package cyral +package instance import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -26,29 +28,29 @@ const ( ErrorKey = "error" ) -func dataSourceSidecarInstance() *schema.Resource { +func DataSourceSidecarInstance() *schema.Resource { return &schema.Resource{ Description: "Retrieve sidecar instances.", - ReadContext: ReadResource(ResourceOperationConfig{ + ReadContext: core.ReadResource(core.ResourceOperationConfig{ Name: "SidecarInstanceDataSourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf( "https://%s/v2/sidecars/%s/instances", - c.ControlPlane, d.Get(SidecarIDKey), + c.ControlPlane, d.Get(utils.SidecarIDKey), ) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &SidecarInstances{} }, }), Schema: map[string]*schema.Schema{ - SidecarIDKey: { + utils.SidecarIDKey: { Description: "Sidecar identifier.", Type: schema.TypeString, Required: true, }, - IDKey: { + utils.IDKey: { Description: "Data source identifier.", Type: schema.TypeString, Computed: true, @@ -59,7 +61,7 @@ func dataSourceSidecarInstance() *schema.Resource { Type: schema.TypeList, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - IDKey: { + utils.IDKey: { Description: "Instance identifier. Varies according to the computing platform that " + "the sidecar is deployed to.", Type: schema.TypeString, @@ -125,7 +127,7 @@ func dataSourceSidecarInstance() *schema.Resource { Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - StatusKey: { + utils.StatusKey: { Description: "Aggregated status of all the sidecar services.", Type: schema.TypeString, Computed: true, @@ -138,7 +140,7 @@ func dataSourceSidecarInstance() *schema.Resource { Type: schema.TypeSet, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - StatusKey: { + utils.StatusKey: { Description: "Aggregated status of sidecar service.", Type: schema.TypeString, Computed: true, @@ -157,12 +159,12 @@ func dataSourceSidecarInstance() *schema.Resource { Type: schema.TypeSet, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - StatusKey: { + utils.StatusKey: { Description: "Component status.", Type: schema.TypeString, Computed: true, }, - DescriptionKey: { + utils.DescriptionKey: { Description: "Describes what the type of check the component represents.", Type: schema.TypeString, Computed: true, @@ -176,7 +178,7 @@ func dataSourceSidecarInstance() *schema.Resource { }, }, }, - HostKey: { + utils.HostKey: { Description: "Service host on the deployment.", Type: schema.TypeString, Computed: true, diff --git a/cyral/data_source_cyral_sidecar_instance_stats.go b/cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance_stats.go similarity index 81% rename from cyral/data_source_cyral_sidecar_instance_stats.go rename to cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance_stats.go index 06793f20..774fc1fd 100644 --- a/cyral/data_source_cyral_sidecar_instance_stats.go +++ b/cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance_stats.go @@ -1,10 +1,12 @@ -package cyral +package instance import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -28,27 +30,27 @@ func (stats *SidecarInstanceStats) WriteToSchema(d *schema.ResourceData) error { return nil } -func dataSourceSidecarInstanceStats() *schema.Resource { +func DataSourceSidecarInstanceStats() *schema.Resource { return &schema.Resource{ Description: "Retrieve sidecar instance statistics. See also data source " + "[`cyral_sidecar_instance`](../data-sources/sidecar_instance.md).", - ReadContext: ReadResource(ResourceOperationConfig{ + ReadContext: core.ReadResource(core.ResourceOperationConfig{ Name: "SidecarInstanceStatsDataSourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf( "https://%s/v2/sidecars/%s/instances/%s/stats", c.ControlPlane, - d.Get(SidecarIDKey), + d.Get(utils.SidecarIDKey), d.Get(InstanceIDKey), ) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &SidecarInstanceStats{} }, }), Schema: map[string]*schema.Schema{ - SidecarIDKey: { + utils.SidecarIDKey: { Description: "Sidecar identifier.", Type: schema.TypeString, Required: true, @@ -59,7 +61,7 @@ func dataSourceSidecarInstanceStats() *schema.Resource { Type: schema.TypeString, Required: true, }, - IDKey: { + utils.IDKey: { Description: fmt.Sprintf("Data source identifier. It's equal to `%s`.", InstanceIDKey), Type: schema.TypeString, Computed: true, diff --git a/cyral/data_source_cyral_sidecar_instance_stats_test.go b/cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance_stats_test.go similarity index 80% rename from cyral/data_source_cyral_sidecar_instance_stats_test.go rename to cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance_stats_test.go index 8a721484..a7255399 100644 --- a/cyral/data_source_cyral_sidecar_instance_stats_test.go +++ b/cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance_stats_test.go @@ -1,10 +1,13 @@ -package cyral +package instance_test import ( "fmt" "regexp" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/instance" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -18,7 +21,7 @@ func TestAccSidecarInstanceStatsDataSource(t *testing.T) { } resource.ParallelTest( t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: testSteps, }, ) @@ -31,7 +34,7 @@ func accTestStepSidecarInstanceStatsDataSource_EmptySidecarID(dataSourceName str `, dataSourceName) return resource.TestStep{ Config: config, - ExpectError: regexp.MustCompile(fmt.Sprintf(`The argument "%s" is required`, SidecarIDKey)), + ExpectError: regexp.MustCompile(fmt.Sprintf(`The argument "%s" is required`, utils.SidecarIDKey)), } } @@ -43,7 +46,7 @@ func accTestStepSidecarInstanceStatsDataSource_EmptyInstanceID(dataSourceName st `, dataSourceName, "some-sidecar-id") return resource.TestStep{ Config: config, - ExpectError: regexp.MustCompile(fmt.Sprintf(`The argument "%s" is required`, InstanceIDKey)), + ExpectError: regexp.MustCompile(fmt.Sprintf(`The argument "%s" is required`, instance.InstanceIDKey)), } } @@ -64,9 +67,9 @@ func accTestStepSidecarInstanceStatsDataSource_NoSidecarFoundForGivenID(dataSour func accTestStepSidecarInstanceStatsDataSource_NoInstanceFoundForGivenID(dataSourceName string) resource.TestStep { // Creates a sidecar that doesn't have any instances, since it was not // deployed. - config := formatBasicSidecarIntoConfig( - basicSidecarResName, - accTestName("data-sidecar-instance-stats", "sidecar"), + config := utils.FormatBasicSidecarIntoConfig( + utils.BasicSidecarResName, + utils.AccTestName("data-sidecar-instance-stats", "sidecar"), "cft-ec2", "", ) @@ -75,7 +78,7 @@ func accTestStepSidecarInstanceStatsDataSource_NoInstanceFoundForGivenID(dataSou sidecar_id = %s instance_id = %q } - `, dataSourceName, basicSidecarID, "some-non-existent-instance-id") + `, dataSourceName, utils.BasicSidecarID, "some-non-existent-instance-id") return resource.TestStep{ Config: config, ExpectError: regexp.MustCompile("instance not found"), diff --git a/cyral/data_source_cyral_sidecar_instance_test.go b/cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance_test.go similarity index 78% rename from cyral/data_source_cyral_sidecar_instance_test.go rename to cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance_test.go index e91dca4f..f03cbd3d 100644 --- a/cyral/data_source_cyral_sidecar_instance_test.go +++ b/cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance_test.go @@ -1,10 +1,13 @@ -package cyral +package instance_test import ( "fmt" "regexp" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/instance" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -21,7 +24,7 @@ func TestAccSidecarInstanceDataSource(t *testing.T) { } resource.ParallelTest( t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: testSteps, }, ) @@ -34,7 +37,7 @@ func accTestStepSidecarInstanceDataSource_EmptySidecarID(dataSourceName string) `, dataSourceName) return resource.TestStep{ Config: config, - ExpectError: regexp.MustCompile(fmt.Sprintf(`The argument "%s" is required`, SidecarIDKey)), + ExpectError: regexp.MustCompile(fmt.Sprintf(`The argument "%s" is required`, utils.SidecarIDKey)), } } @@ -54,9 +57,9 @@ func accTestStepSidecarInstanceDataSource_NoSidecarFoundForGivenID(dataSourceNam func accTestStepSidecarInstanceDataSource_NoSidecarInstances(dataSourceName string) resource.TestStep { // Creates a sidecar that doesn't have any instances, since it was not // deployed. - config := formatBasicSidecarIntoConfig( - basicSidecarResName, - accTestName("data-sidecar-instance", "sidecar"), + config := utils.FormatBasicSidecarIntoConfig( + utils.BasicSidecarResName, + utils.AccTestName("data-sidecar-instance", "sidecar"), "cft-ec2", "", ) @@ -64,20 +67,20 @@ func accTestStepSidecarInstanceDataSource_NoSidecarInstances(dataSourceName stri data "cyral_sidecar_instance" "%s" { sidecar_id = %s } - `, dataSourceName, basicSidecarID) + `, dataSourceName, utils.BasicSidecarID) dataSourceFullName := fmt.Sprintf(sidecarInstanceDataSourceFullNameFmt, dataSourceName) check := resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet( dataSourceFullName, - SidecarIDKey, + utils.SidecarIDKey, ), resource.TestCheckResourceAttrSet( dataSourceFullName, - IDKey, + utils.IDKey, ), resource.TestCheckResourceAttr( dataSourceFullName, - fmt.Sprintf("%s.#", SidecarInstanceListKey), + fmt.Sprintf("%s.#", instance.SidecarInstanceListKey), "0", ), ) diff --git a/cyral/model_sidecar_instance.go b/cyral/internal/sidecar/instance/model_sidecar_instance.go similarity index 88% rename from cyral/model_sidecar_instance.go rename to cyral/internal/sidecar/instance/model_sidecar_instance.go index c7597860..f03e4a88 100644 --- a/cyral/model_sidecar_instance.go +++ b/cyral/internal/sidecar/instance/model_sidecar_instance.go @@ -1,6 +1,7 @@ -package cyral +package instance import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -31,7 +32,7 @@ type SidecarInstance struct { func (instance *SidecarInstance) ToMap() map[string]any { return map[string]any{ - IDKey: instance.ID, + utils.IDKey: instance.ID, MetadataKey: instance.Metadata.ToInterfaceList(), MonitoringKey: instance.Monitoring.ToInterfaceList(), } @@ -89,8 +90,8 @@ func (monitoring *SidecarInstanceMonitoring) ToInterfaceList() []any { } return []any{ map[string]any{ - StatusKey: monitoring.Status, - ServicesKey: services, + utils.StatusKey: monitoring.Status, + ServicesKey: services, }, } } @@ -111,10 +112,10 @@ func (service *SidecarService) ToMap() map[string]any { components[componentKey] = component.ToMap() } return map[string]any{ - StatusKey: service.Status, - MetricsPortKey: service.MetricsPort, - ComponentsKey: components, - HostKey: service.Host, + utils.StatusKey: service.Status, + MetricsPortKey: service.MetricsPort, + ComponentsKey: components, + utils.HostKey: service.Host, } } @@ -126,8 +127,8 @@ type SidecarServiceComponent struct { func (component *SidecarServiceComponent) ToMap() map[string]any { return map[string]any{ - StatusKey: component.Status, - DescriptionKey: component.Description, - ErrorKey: component.Error, + utils.StatusKey: component.Status, + utils.DescriptionKey: component.Description, + ErrorKey: component.Error, } } diff --git a/cyral/data_source_cyral_sidecar_listener.go b/cyral/internal/sidecar/listener/data_source_cyral_sidecar_listener.go similarity index 74% rename from cyral/data_source_cyral_sidecar_listener.go rename to cyral/internal/sidecar/listener/data_source_cyral_sidecar_listener.go index 894fc067..5106210c 100644 --- a/cyral/data_source_cyral_sidecar_listener.go +++ b/cyral/internal/sidecar/listener/data_source_cyral_sidecar_listener.go @@ -1,4 +1,4 @@ -package cyral +package listener import ( "fmt" @@ -9,7 +9,10 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "golang.org/x/exp/slices" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" ) const ( @@ -27,14 +30,14 @@ func (data ReadDataSourceSidecarListenerAPIResponse) WriteToSchema(d *schema.Res log.Printf("[DEBUG] data.ListenerConfig: %+v", data.ListenerConfigs) log.Printf("[DEBUG] Init for _, l := range data.ListenerConfig") repoTypeFilter := d.Get(DSRepoTypeKey).(string) - portFilter := d.Get(PortKey).(int) + portFilter := d.Get(utils.PortKey).(int) for _, listenerConfig := range data.ListenerConfigs { // Check if either the repo filter or the port filter is provided and matches the listener if (repoTypeFilter == "" || slices.Contains(listenerConfig.RepoTypes, repoTypeFilter)) && (portFilter == 0 || listenerConfig.NetworkAddress.Port == portFilter) { listener := map[string]any{ - ListenerIDKey: listenerConfig.ListenerId, - SidecarIDKey: d.Get(SidecarIDKey).(string), + utils.ListenerIDKey: listenerConfig.ListenerId, + utils.SidecarIDKey: d.Get(utils.SidecarIDKey).(string), RepoTypesKey: listenerConfig.RepoTypes, NetworkAddressKey: listenerConfig.NetworkAddressAsInterface(), MySQLSettingsKey: listenerConfig.MySQLSettingsAsInterface(), @@ -61,39 +64,39 @@ func (data ReadDataSourceSidecarListenerAPIResponse) WriteToSchema(d *schema.Res return nil } -func dataSourceSidecarListenerReadConfig() ResourceOperationConfig { - return ResourceOperationConfig{ +func dataSourceSidecarListenerReadConfig() core.ResourceOperationConfig { + return core.ResourceOperationConfig{ Name: "SidecarListenerDataSourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { - sidecarID := d.Get(SidecarIDKey).(string) + sidecarID := d.Get(utils.SidecarIDKey).(string) return fmt.Sprintf("https://%s/v1/sidecars/%s/listeners", c.ControlPlane, sidecarID) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &ReadDataSourceSidecarListenerAPIResponse{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &ReadDataSourceSidecarListenerAPIResponse{} }, } } -func dataSourceSidecarListener() *schema.Resource { +func DataSourceSidecarListener() *schema.Resource { log.Printf("[DEBUG] Init dataSourceSidecarListener") - listenerSchema := convertSchemaFieldsToComputed(getSidecarListenerSchema()) + listenerSchema := utils.ConvertSchemaFieldsToComputed(getSidecarListenerSchema()) log.Printf("[DEBUG] End dataSourceSidecarListener") return &schema.Resource{ Description: "Retrieve and filter sidecar listeners.", - ReadContext: ReadResource(dataSourceSidecarListenerReadConfig()), + ReadContext: core.ReadResource(dataSourceSidecarListenerReadConfig()), Schema: map[string]*schema.Schema{ - SidecarIDKey: { + utils.SidecarIDKey: { Description: "Filter the results by sidecar ID.", Type: schema.TypeString, Required: true, }, DSRepoTypeKey: { - Description: "Filter the results per repository type. Supported repo types:" + supportedTypesMarkdown(repositoryTypes()), + Description: "Filter the results per repository type. Supported repo types:" + utils.SupportedValuesAsMarkdown(repository.RepositoryTypes()), Type: schema.TypeString, Optional: true, }, - PortKey: { + utils.PortKey: { Description: "Filter the results per port.", Type: schema.TypeInt, Optional: true, diff --git a/cyral/data_source_cyral_sidecar_listener_test.go b/cyral/internal/sidecar/listener/data_source_cyral_sidecar_listener_test.go similarity index 66% rename from cyral/data_source_cyral_sidecar_listener_test.go rename to cyral/internal/sidecar/listener/data_source_cyral_sidecar_listener_test.go index 8e9ab696..eb2994c6 100644 --- a/cyral/data_source_cyral_sidecar_listener_test.go +++ b/cyral/internal/sidecar/listener/data_source_cyral_sidecar_listener_test.go @@ -1,10 +1,14 @@ -package cyral +package listener_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/listener" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "golang.org/x/exp/slices" ) @@ -12,23 +16,23 @@ const ( listenerDataSourceName = "data-repository" ) -func listenerDataSourceTestRepos() []SidecarListener { - return []SidecarListener{ +func listenerDataSourceTestRepos() []listener.SidecarListener { + return []listener.SidecarListener{ { RepoTypes: []string{"mysql"}, - NetworkAddress: &NetworkAddress{ + NetworkAddress: &listener.NetworkAddress{ Port: 3306, }, }, { RepoTypes: []string{"mongodb"}, - NetworkAddress: &NetworkAddress{ + NetworkAddress: &listener.NetworkAddress{ Port: 27017, }, }, { RepoTypes: []string{"mongodb"}, - NetworkAddress: &NetworkAddress{ + NetworkAddress: &listener.NetworkAddress{ Port: 27018, }, }, @@ -48,7 +52,11 @@ func TestAccSidecarListenerDataSource(t *testing.T) { testListeners, testListeners[2].RepoTypes[0], testListeners[0].NetworkAddress.Port) resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return provider.Provider(), nil + }, + }, Steps: []resource.TestStep{ { Config: testConfigTypeFilter, @@ -66,24 +74,24 @@ func TestAccSidecarListenerDataSource(t *testing.T) { }) } -func testListenerDataSource(listeners []SidecarListener, repoTypeFilter string, portFilter int) ( +func testListenerDataSource(listeners []listener.SidecarListener, repoTypeFilter string, portFilter int) ( string, resource.TestCheckFunc, ) { return testListenerDataSourceConfig(listeners, repoTypeFilter, portFilter), testListenerDataSourceChecks(listeners, repoTypeFilter, portFilter) } -func testListenerDataSourceConfig(listeners []SidecarListener, repoTypeFilter string, portFilter int) string { +func testListenerDataSourceConfig(listeners []listener.SidecarListener, repoTypeFilter string, portFilter int) string { var config string var dependsOn []string for _, listener := range listeners { resourceName := fmt.Sprintf("%s_%d", listener.RepoTypes[0], listener.NetworkAddress.Port) - config += setupSidecarListenerConfig(resourceName, listener) + config += SetupSidecarListenerConfig(resourceName, listener) dependsOn = append(dependsOn, fmt.Sprintf("cyral_sidecar_listener.%s", resourceName)) } - sidecarConfig := formatBasicSidecarIntoConfig( - basicSidecarResName, - accTestName("ds-sidecar-listener", "sidecar"), + sidecarConfig := utils.FormatBasicSidecarIntoConfig( + utils.BasicSidecarResName, + utils.AccTestName("ds-sidecar-listener", "sidecar"), "docker", "", ) config += sidecarConfig + listenerDataSourceConfig(repoTypeFilter, portFilter, dependsOn) @@ -91,7 +99,7 @@ func testListenerDataSourceConfig(listeners []SidecarListener, repoTypeFilter st return config } -func testListenerDataSourceChecks(listeners []SidecarListener, repoTypeFilter string, portFilter int) resource.TestCheckFunc { +func testListenerDataSourceChecks(listeners []listener.SidecarListener, repoTypeFilter string, portFilter int) resource.TestCheckFunc { dataSourceFullName := "data.cyral_sidecar_listener.test_listener" if repoTypeFilter == "" && portFilter == 0 { @@ -113,8 +121,8 @@ func testListenerDataSourceChecks(listeners []SidecarListener, repoTypeFilter st return resource.ComposeTestCheckFunc(checkFuncs...) } -func filterListenerData(listeners []SidecarListener, repoTypeFilter string, portFilter int) []SidecarListener { - var filteredListeners []SidecarListener +func filterListenerData(listeners []listener.SidecarListener, repoTypeFilter string, portFilter int) []listener.SidecarListener { + var filteredListeners []listener.SidecarListener for _, l := range listeners { if (repoTypeFilter == "" || slices.Contains(l.RepoTypes, repoTypeFilter)) && (portFilter == 0 || l.NetworkAddress.Port == portFilter) { @@ -132,20 +140,20 @@ func listenerDataSourceConfig(repoTypeFilter string, portFilter int, dependsOn [ sidecar_id = %s repo_type = "%s" port = %d - }`, listToStr(dependsOn), basicSidecarID, repoTypeFilter, portFilter) + }`, utils.ListToStr(dependsOn), utils.BasicSidecarID, repoTypeFilter, portFilter) } else if repoTypeFilter != "" { return fmt.Sprintf(` data "cyral_sidecar_listener" "test_listener" { depends_on = %s sidecar_id = %s repo_type = "%s" - }`, listToStr(dependsOn), basicSidecarID, repoTypeFilter) + }`, utils.ListToStr(dependsOn), utils.BasicSidecarID, repoTypeFilter) } else { return fmt.Sprintf(` data "cyral_sidecar_listener" "test_listener" { depends_on = %s sidecar_id = %s port = %d - }`, listToStr(dependsOn), basicSidecarID, portFilter) + }`, utils.ListToStr(dependsOn), utils.BasicSidecarID, portFilter) } } diff --git a/cyral/resource_cyral_sidecar_listener.go b/cyral/internal/sidecar/listener/resource_cyral_sidecar_listener.go similarity index 87% rename from cyral/resource_cyral_sidecar_listener.go rename to cyral/internal/sidecar/listener/resource_cyral_sidecar_listener.go index caa15e7a..51bdc4fe 100644 --- a/cyral/resource_cyral_sidecar_listener.go +++ b/cyral/internal/sidecar/listener/resource_cyral_sidecar_listener.go @@ -1,4 +1,4 @@ -package cyral +package listener import ( "context" @@ -8,7 +8,10 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" ) // create a constant block for schema keys @@ -56,17 +59,17 @@ type SQLServerSettings struct { Version string `json:"version,omitempty"` } -var ReadSidecarListenersConfig = ResourceOperationConfig{ +var ReadSidecarListenersConfig = core.ResourceOperationConfig{ Name: "SidecarListenersResourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/sidecars/%s/listeners/%s", c.ControlPlane, - d.Get(SidecarIDKey).(string), - d.Get(ListenerIDKey).(string)) + d.Get(utils.SidecarIDKey).(string), + d.Get(utils.ListenerIDKey).(string)) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &ReadSidecarListenerAPIResponse{} }, - RequestErrorHandler: &ReadIgnoreHttpNotFound{resName: "Sidecar listener"}, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &ReadSidecarListenerAPIResponse{} }, + RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Sidecar listener"}, } type ReadSidecarListenerAPIResponse struct { @@ -76,15 +79,15 @@ type CreateListenerAPIResponse struct { ListenerId string `json:"listenerId"` } -func (response CreateListenerAPIResponse) WriteToSchema(d *schema.ResourceData) error { - d.SetId(marshalComposedID([]string{d.Get(SidecarIDKey).(string), response.ListenerId}, "/")) - return d.Set(ListenerIDKey, response.ListenerId) +func (c CreateListenerAPIResponse) WriteToSchema(d *schema.ResourceData) error { + d.SetId(utils.MarshalComposedID([]string{d.Get(utils.SidecarIDKey).(string), c.ListenerId}, "/")) + return d.Set(utils.ListenerIDKey, c.ListenerId) } func (data ReadSidecarListenerAPIResponse) WriteToSchema(d *schema.ResourceData) error { log.Printf("[DEBUG] Init ReadSidecarListenerAPIResponse.WriteToSchema") if data.ListenerConfig != nil { - _ = d.Set(ListenerIDKey, data.ListenerConfig.ListenerId) + _ = d.Set(utils.ListenerIDKey, data.ListenerConfig.ListenerId) _ = d.Set(RepoTypesKey, data.ListenerConfig.RepoTypesAsInterface()) _ = d.Set(NetworkAddressKey, data.ListenerConfig.NetworkAddressAsInterface()) _ = d.Set(S3SettingsKey, data.ListenerConfig.S3SettingsAsInterface()) @@ -119,8 +122,8 @@ func (l *SidecarListener) NetworkAddressAsInterface() []interface{} { } return []interface{}{ map[string]interface{}{ - HostKey: l.NetworkAddress.Host, - PortKey: l.NetworkAddress.Port, + utils.HostKey: l.NetworkAddress.Host, + utils.PortKey: l.NetworkAddress.Port, }, } } @@ -129,8 +132,8 @@ func (l *SidecarListener) NetworkAddressFromInterface(anInterface []interface{}) return } l.NetworkAddress = &NetworkAddress{ - Host: anInterface[0].(map[string]interface{})[HostKey].(string), - Port: anInterface[0].(map[string]interface{})[PortKey].(int), + Host: anInterface[0].(map[string]interface{})[utils.HostKey].(string), + Port: anInterface[0].(map[string]interface{})[utils.PortKey].(int), } } func (l *SidecarListener) MySQLSettingsAsInterface() []interface{} { @@ -208,8 +211,8 @@ type SidecarListenerResource struct { // ReadFromSchema populates the SidecarListenerResource from the schema func (s *SidecarListenerResource) ReadFromSchema(d *schema.ResourceData) error { s.ListenerConfig = SidecarListener{ - SidecarId: d.Get(SidecarIDKey).(string), - ListenerId: d.Get(ListenerIDKey).(string), + SidecarId: d.Get(utils.SidecarIDKey).(string), + ListenerId: d.Get(utils.ListenerIDKey).(string), } s.ListenerConfig.RepoTypesFromInterface(d.Get(RepoTypesKey).([]interface{})) s.ListenerConfig.NetworkAddressFromInterface(d.Get(NetworkAddressKey).(*schema.Set).List()) @@ -227,49 +230,49 @@ func (s *SidecarListenerResource) ReadFromSchema(d *schema.ResourceData) error { // POST {{baseURL}}/sidecars/:sidecarID/listeners/ (Create a listener) // PUT {{baseURL}}/sidecars/:sidecarID/listeners/:listenerID (Update a listener) // DELETE {{baseURL}}/sidecars/:sidecarID/listeners/:listenerID (Delete a listener) -func resourceSidecarListener() *schema.Resource { +func ResourceSidecarListener() *schema.Resource { return &schema.Resource{ Description: "Manages [sidecar listeners](https://cyral.com/docs/sidecars/sidecar-listeners)." + "\n~> **Warning** Multiple listeners can be associated to a single sidecar as long as " + "`host` and `port` are unique. If `host` is omitted, then `port` must be unique.", - CreateContext: CreateResource( - ResourceOperationConfig{ + CreateContext: core.CreateResource( + core.ResourceOperationConfig{ Name: "SidecarListenersResourceCreate", HttpMethod: http.MethodPost, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/sidecars/%s/listeners", c.ControlPlane, - d.Get(SidecarIDKey).(string)) + d.Get(utils.SidecarIDKey).(string)) }, - NewResourceData: func() ResourceData { return &SidecarListenerResource{} }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { return &CreateListenerAPIResponse{} }, + NewResourceData: func() core.ResourceData { return &SidecarListenerResource{} }, + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &CreateListenerAPIResponse{} }, }, ReadSidecarListenersConfig, ), - ReadContext: ReadResource(ReadSidecarListenersConfig), - UpdateContext: UpdateResource( - ResourceOperationConfig{ + ReadContext: core.ReadResource(ReadSidecarListenersConfig), + UpdateContext: core.UpdateResource( + core.ResourceOperationConfig{ Name: "SidecarListenersResourceUpdate", HttpMethod: http.MethodPut, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/sidecars/%s/listeners/%s", c.ControlPlane, - d.Get(SidecarIDKey).(string), - d.Get(ListenerIDKey).(string)) + d.Get(utils.SidecarIDKey).(string), + d.Get(utils.ListenerIDKey).(string)) }, - NewResourceData: func() ResourceData { return &SidecarListenerResource{} }, + NewResourceData: func() core.ResourceData { return &SidecarListenerResource{} }, }, ReadSidecarListenersConfig, ), - DeleteContext: DeleteResource( - ResourceOperationConfig{ + DeleteContext: core.DeleteResource( + core.ResourceOperationConfig{ Name: "SidecarListenersResourceDelete", HttpMethod: http.MethodDelete, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/sidecars/%s/listeners/%s", c.ControlPlane, - d.Get(SidecarIDKey).(string), - d.Get(ListenerIDKey).(string)) + d.Get(utils.SidecarIDKey).(string), + d.Get(utils.ListenerIDKey).(string)) }, }, ), @@ -280,12 +283,12 @@ func resourceSidecarListener() *schema.Resource { d *schema.ResourceData, m interface{}, ) ([]*schema.ResourceData, error) { - ids, err := unmarshalComposedID(d.Id(), "/", 2) + ids, err := utils.UnMarshalComposedID(d.Id(), "/", 2) if err != nil { return nil, err } - _ = d.Set(SidecarIDKey, ids[0]) - _ = d.Set(ListenerIDKey, ids[1]) + _ = d.Set(utils.SidecarIDKey, ids[0]) + _ = d.Set(utils.ListenerIDKey, ids[1]) return []*schema.ResourceData{d}, nil }, }, @@ -294,20 +297,20 @@ func resourceSidecarListener() *schema.Resource { func getSidecarListenerSchema() map[string]*schema.Schema { return map[string]*schema.Schema{ - ListenerIDKey: { + utils.ListenerIDKey: { Description: "ID of the listener that will be bound to the sidecar.", Type: schema.TypeString, ForceNew: true, Computed: true, }, - SidecarIDKey: { + utils.SidecarIDKey: { Description: "ID of the sidecar that the listener will be bound to.", Type: schema.TypeString, Required: true, ForceNew: true, }, RepoTypesKey: { - Description: "List of repository types that the listener supports. Currently limited to one repo type from supported repo types:" + supportedTypesMarkdown(repositoryTypes()), + Description: "List of repository types that the listener supports. Currently limited to one repo type from supported repo types:" + utils.SupportedValuesAsMarkdown(repository.RepositoryTypes()), Type: schema.TypeList, Required: true, Elem: &schema.Schema{ @@ -321,13 +324,13 @@ func getSidecarListenerSchema() map[string]*schema.Schema { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - HostKey: { + utils.HostKey: { Description: "Host where the sidecar will listen for the given repository, in the case where the sidecar is deployed on a host " + "with multiple network interfaces. If omitted, the sidecar will assume the default \"0.0.0.0\" and listen on all network interfaces.", Type: schema.TypeString, Optional: true, }, - PortKey: { + utils.PortKey: { Description: "Port where the sidecar will listen for the given repository.", Type: schema.TypeInt, Required: true, diff --git a/cyral/resource_cyral_sidecar_listener_test.go b/cyral/internal/sidecar/listener/resource_cyral_sidecar_listener_test.go similarity index 73% rename from cyral/resource_cyral_sidecar_listener_test.go rename to cyral/internal/sidecar/listener/resource_cyral_sidecar_listener_test.go index 26b2c34d..927b2aad 100644 --- a/cyral/resource_cyral_sidecar_listener_test.go +++ b/cyral/internal/sidecar/listener/resource_cyral_sidecar_listener_test.go @@ -1,4 +1,4 @@ -package cyral +package listener_test import ( "fmt" @@ -6,6 +6,9 @@ import ( "strconv" "testing" + listen "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/listener" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -14,9 +17,9 @@ const ( ) func sidecarListenerSidecarConfig() string { - return formatBasicSidecarIntoConfig( - basicSidecarResName, - accTestName(sidecarListenerTestSidecarResourceName, "sidecar"), + return utils.FormatBasicSidecarIntoConfig( + utils.BasicSidecarResName, + utils.AccTestName(sidecarListenerTestSidecarResourceName, "sidecar"), "docker", "", ) } @@ -29,7 +32,7 @@ func TestSidecarListenerResource(t *testing.T) { resource.ParallelTest( t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: testSteps, }, ) @@ -37,35 +40,35 @@ func TestSidecarListenerResource(t *testing.T) { func updateTest() []resource.TestStep { // Start with a bare bones mySQL sidecar listener - onlyRequiredFields := SidecarListener{ + onlyRequiredFields := listen.SidecarListener{ RepoTypes: []string{"mysql"}, - NetworkAddress: &NetworkAddress{ + NetworkAddress: &listen.NetworkAddress{ Port: 8000, }, } // Change port. - changePort := SidecarListener{ + changePort := listen.SidecarListener{ RepoTypes: []string{"mysql"}, - NetworkAddress: &NetworkAddress{ + NetworkAddress: &listen.NetworkAddress{ Port: 443, }, } // Add host. - addHost := SidecarListener{ + addHost := listen.SidecarListener{ RepoTypes: []string{"mysql"}, - NetworkAddress: &NetworkAddress{ + NetworkAddress: &listen.NetworkAddress{ Port: 443, Host: "https://mysql.test.com", }, } // Add settings. - addSettings := SidecarListener{ + addSettings := listen.SidecarListener{ RepoTypes: []string{"mysql"}, - NetworkAddress: &NetworkAddress{ + NetworkAddress: &listen.NetworkAddress{ Port: 443, Host: "https://s3.test.com", }, - MySQLSettings: &MySQLSettings{ + MySQLSettings: &listen.MySQLSettings{ DbVersion: "3.4.0", }, } @@ -92,58 +95,58 @@ func updateTest() []resource.TestStep { func settingsTest() []resource.TestStep { // MySQL listener with no character set defined. - mySQLNoCharSet := SidecarListener{ + mySQLNoCharSet := listen.SidecarListener{ RepoTypes: []string{"mysql"}, - NetworkAddress: &NetworkAddress{ + NetworkAddress: &listen.NetworkAddress{ Port: 8000, Host: "https://mysql.test.com", }, - MySQLSettings: &MySQLSettings{ + MySQLSettings: &listen.MySQLSettings{ DbVersion: "2.1.0", }, } // MySQL listener with character set defined. - mySQLWithCharSet := SidecarListener{ + mySQLWithCharSet := listen.SidecarListener{ RepoTypes: []string{"mysql"}, - NetworkAddress: &NetworkAddress{ + NetworkAddress: &listen.NetworkAddress{ Port: 8001, Host: "https://mysql.test.com", }, - MySQLSettings: &MySQLSettings{ + MySQLSettings: &listen.MySQLSettings{ DbVersion: "2.1.0", CharacterSet: "ujis_japanese_ci", }, } // S3 listeners with proxy mode. - s3 := SidecarListener{ + s3 := listen.SidecarListener{ RepoTypes: []string{"s3"}, - NetworkAddress: &NetworkAddress{ + NetworkAddress: &listen.NetworkAddress{ Port: 8002, Host: "https://mysql.test.com", }, - S3Settings: &S3Settings{ + S3Settings: &listen.S3Settings{ ProxyMode: true, }, } // DynamoDB with proxy mode (required for all DynamoDB repo types). - dynamodb := SidecarListener{ + dynamodb := listen.SidecarListener{ RepoTypes: []string{"dynamodb"}, - NetworkAddress: &NetworkAddress{ + NetworkAddress: &listen.NetworkAddress{ Port: 8003, Host: "https://mysql.test.com", }, - DynamoDbSettings: &DynamoDbSettings{ + DynamoDbSettings: &listen.DynamoDbSettings{ ProxyMode: true, }, } // SQL Server settings test step - sqlServerSettings := SidecarListener{ + sqlServerSettings := listen.SidecarListener{ RepoTypes: []string{"sqlserver"}, - NetworkAddress: &NetworkAddress{ + NetworkAddress: &listen.NetworkAddress{ Port: 8004, Host: "https://sqlserver.test.com", }, - SQLServerSettings: &SQLServerSettings{ + SQLServerSettings: &listen.SQLServerSettings{ Version: "16.0.1000", }, } @@ -175,26 +178,26 @@ func settingsTest() []resource.TestStep { func multipleListenersAndImportTest() []resource.TestStep { // Define first listener resource. listener1ResName := "listener1-multiple-test" - listener1 := SidecarListener{ + listener1 := listen.SidecarListener{ RepoTypes: []string{"oracle"}, - NetworkAddress: &NetworkAddress{ + NetworkAddress: &listen.NetworkAddress{ Port: 6003, }, } // Define second listener resource. listener2ResName := "listener2-multiple-test" - listener2 := SidecarListener{ + listener2 := listen.SidecarListener{ RepoTypes: []string{"postgresql"}, - NetworkAddress: &NetworkAddress{ + NetworkAddress: &listen.NetworkAddress{ Port: 6018, }, } // Setup config containing both listeners. - listener1Config := setupSidecarListenerConfig( + listener1Config := SetupSidecarListenerConfig( listener1ResName, listener1, ) - listener2Config := setupSidecarListenerConfig( + listener2Config := SetupSidecarListenerConfig( listener2ResName, listener2, ) multipleListenersConfig := sidecarListenerSidecarConfig() + @@ -243,9 +246,9 @@ func settingsConflictsTest() []resource.TestStep { for j := i + 1; j < len(repoTypes); j++ { // Create a listener with all conflicting repo types // Downstream test code will cut at [0], but this is fine for what we are testing here - listener := SidecarListener{ + listener := listen.SidecarListener{ RepoTypes: repoTypes, - NetworkAddress: &NetworkAddress{ + NetworkAddress: &listen.NetworkAddress{ Port: 8000, Host: "https://mysql.test.com", }, @@ -255,7 +258,7 @@ func settingsConflictsTest() []resource.TestStep { appendSetting(&listener, repoTypes[j]) // Create a test step with the listener testSteps = append(testSteps, resource.TestStep{ - Config: sidecarListenerSidecarConfig() + setupSidecarListenerConfig("settings_conflict", listener), + Config: sidecarListenerSidecarConfig() + SetupSidecarListenerConfig("settings_conflict", listener), ExpectError: regexp.MustCompile( ".*conflicts with.*", ), @@ -265,38 +268,38 @@ func settingsConflictsTest() []resource.TestStep { return testSteps } -// appendSetting applies settings for a given repository type to the provided listener. +// appendSetting applies settings for a given repository type to the provided listen. // The listener's repository types are updated accordingly using default values. -func appendSetting(listener *SidecarListener, repoType string) { +func appendSetting(l *listen.SidecarListener, repoType string) { switch repoType { case "mysql": - listener.MySQLSettings = &MySQLSettings{ + l.MySQLSettings = &listen.MySQLSettings{ DbVersion: "5.7", } case "s3": - listener.S3Settings = &S3Settings{ + l.S3Settings = &listen.S3Settings{ ProxyMode: true, } case "dynamodb": - listener.DynamoDbSettings = &DynamoDbSettings{ + l.DynamoDbSettings = &listen.DynamoDbSettings{ ProxyMode: true, } case "sqlserver": - listener.SQLServerSettings = &SQLServerSettings{ + l.SQLServerSettings = &listen.SQLServerSettings{ Version: "16.0.1000", } } } -func setupSidecarListenerTestStep(resName string, listener SidecarListener) resource.TestStep { +func setupSidecarListenerTestStep(resName string, listener listen.SidecarListener) resource.TestStep { return resource.TestStep{ Config: sidecarListenerSidecarConfig() + - setupSidecarListenerConfig(resName, listener), + SetupSidecarListenerConfig(resName, listener), Check: setupSidecarListenerCheck(resName, listener), } } -func setupSidecarListenerCheck(resourceName string, listener SidecarListener) resource.TestCheckFunc { +func setupSidecarListenerCheck(resourceName string, listener listen.SidecarListener) resource.TestCheckFunc { resFullName := fmt.Sprintf("cyral_sidecar_listener.%s", resourceName) var checkFuncs []resource.TestCheckFunc @@ -304,12 +307,12 @@ func setupSidecarListenerCheck(resourceName string, listener SidecarListener) re checkFuncs = append( checkFuncs, []resource.TestCheckFunc{ resource.TestCheckResourceAttrPair( - resFullName, SidecarIDKey, - fmt.Sprintf("cyral_sidecar.%s", basicSidecarResName), "id", + resFullName, utils.SidecarIDKey, + fmt.Sprintf("cyral_sidecar.%s", utils.BasicSidecarResName), "id", ), resource.TestCheckResourceAttr( resFullName, - fmt.Sprintf("%s.0", RepoTypesKey), listener.RepoTypes[0], + fmt.Sprintf("%s.0", listen.RepoTypesKey), listener.RepoTypes[0], ), }..., ) @@ -321,19 +324,19 @@ func setupSidecarListenerCheck(resourceName string, listener SidecarListener) re // Exactly one Network Address conf. resource.TestCheckResourceAttr( resFullName, - fmt.Sprintf("%s.#", NetworkAddressKey), + fmt.Sprintf("%s.#", listen.NetworkAddressKey), "1", ), // Check host. resource.TestCheckResourceAttr( resFullName, - fmt.Sprintf("%s.0.%s", NetworkAddressKey, HostKey), + fmt.Sprintf("%s.0.%s", listen.NetworkAddressKey, utils.HostKey), listener.NetworkAddress.Host, ), // Check port. resource.TestCheckResourceAttr( resFullName, - fmt.Sprintf("%s.0.%s", NetworkAddressKey, PortKey), + fmt.Sprintf("%s.0.%s", listen.NetworkAddressKey, utils.PortKey), strconv.Itoa(listener.NetworkAddress.Port), ), }..., @@ -351,19 +354,19 @@ func setupSidecarListenerCheck(resourceName string, listener SidecarListener) re // Exactly one mySQL Settings. resource.TestCheckResourceAttr( resFullName, - fmt.Sprintf("%s.#", MySQLSettingsKey), + fmt.Sprintf("%s.#", listen.MySQLSettingsKey), "1", ), // Check DB version. resource.TestCheckResourceAttr( resFullName, - fmt.Sprintf("%s.0.%s", MySQLSettingsKey, DbVersionKey), + fmt.Sprintf("%s.0.%s", listen.MySQLSettingsKey, listen.DbVersionKey), listener.MySQLSettings.DbVersion, ), // Check character set. resource.TestCheckResourceAttr( resFullName, - fmt.Sprintf("%s.0.%s", MySQLSettingsKey, CharacterSetKey), + fmt.Sprintf("%s.0.%s", listen.MySQLSettingsKey, listen.CharacterSetKey), expectedCharSet, ), }..., @@ -376,13 +379,13 @@ func setupSidecarListenerCheck(resourceName string, listener SidecarListener) re // Exactly one S3 Settings. resource.TestCheckResourceAttr( resFullName, - fmt.Sprintf("%s.#", S3SettingsKey), + fmt.Sprintf("%s.#", listen.S3SettingsKey), "1", ), // Check proxy mode. resource.TestCheckResourceAttr( resFullName, - fmt.Sprintf("%s.0.%s", S3SettingsKey, ProxyModeKey), + fmt.Sprintf("%s.0.%s", listen.S3SettingsKey, listen.ProxyModeKey), strconv.FormatBool(listener.S3Settings.ProxyMode), ), }..., @@ -395,13 +398,13 @@ func setupSidecarListenerCheck(resourceName string, listener SidecarListener) re // Exactly one S3 Settings. resource.TestCheckResourceAttr( resFullName, - fmt.Sprintf("%s.#", DynamoDbSettingsKey), + fmt.Sprintf("%s.#", listen.DynamoDbSettingsKey), "1", ), // Check proxy mode. resource.TestCheckResourceAttr( resFullName, - fmt.Sprintf("%s.0.%s", DynamoDbSettingsKey, ProxyModeKey), + fmt.Sprintf("%s.0.%s", listen.DynamoDbSettingsKey, listen.ProxyModeKey), strconv.FormatBool(listener.DynamoDbSettings.ProxyMode), ), }..., @@ -411,7 +414,7 @@ func setupSidecarListenerCheck(resourceName string, listener SidecarListener) re return resource.ComposeTestCheckFunc(checkFuncs...) } -func setupSidecarListenerConfig(resourceName string, listener SidecarListener) string { +func SetupSidecarListenerConfig(resourceName string, listener listen.SidecarListener) string { var config string var networkAddressStr string if listener.NetworkAddress != nil { @@ -483,7 +486,7 @@ func setupSidecarListenerConfig(resourceName string, listener SidecarListener) s repo_types = ["%s"] %s %s - }`, resourceName, basicSidecarID, listener.RepoTypes[0], networkAddressStr, settings, + }`, resourceName, utils.BasicSidecarID, listener.RepoTypes[0], networkAddressStr, settings, ) return config } diff --git a/cyral/resource_cyral_sidecar.go b/cyral/internal/sidecar/resource_cyral_sidecar.go similarity index 94% rename from cyral/resource_cyral_sidecar.go rename to cyral/internal/sidecar/resource_cyral_sidecar.go index 0f5af36a..b77d3153 100644 --- a/cyral/resource_cyral_sidecar.go +++ b/cyral/internal/sidecar/resource_cyral_sidecar.go @@ -1,4 +1,4 @@ -package cyral +package sidecar import ( "context" @@ -12,7 +12,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" ) type CreateSidecarResponse struct { @@ -64,7 +65,7 @@ type CertificateBundleSecret struct { Type string `json:"type,omitempty"` } -func resourceSidecar() *schema.Resource { +func ResourceSidecar() *schema.Resource { return &schema.Resource{ Description: "Manages [sidecars](https://cyral.com/docs/sidecars/sidecar-manage).", CreateContext: resourceSidecarCreate, @@ -198,19 +199,19 @@ func resourceSidecarCreate(ctx context.Context, d *schema.ResourceData, m interf resourceData, err := getSidecarDataFromResource(c, d) if err != nil { - return createError("Unable to create sidecar", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to create sidecar", fmt.Sprintf("%v", err)) } url := fmt.Sprintf("https://%s/v1/sidecars", c.ControlPlane) body, err := c.DoRequest(url, http.MethodPost, resourceData) if err != nil { - return createError("Unable to create sidecar", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to create sidecar", fmt.Sprintf("%v", err)) } response := SidecarData{} if err := json.Unmarshal(body, &response); err != nil { - return createError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] Response body (unmarshalled): %#v", response) @@ -248,7 +249,7 @@ func resourceSidecarRead(ctx context.Context, d *schema.ResourceData, m interfac return nil } - return createError( + return utils.CreateError( fmt.Sprintf( "Unable to read sidecar. SidecarID: %s", d.Id(), @@ -258,7 +259,7 @@ func resourceSidecarRead(ctx context.Context, d *schema.ResourceData, m interfac response := SidecarData{} if err := json.Unmarshal(body, &response); err != nil { - return createError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] Response body (unmarshalled): %#v", response) @@ -286,13 +287,13 @@ func resourceSidecarUpdate(ctx context.Context, d *schema.ResourceData, m interf resourceData, err := getSidecarDataFromResource(c, d) if err != nil { - return createError("Unable to update sidecar", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to update sidecar", fmt.Sprintf("%v", err)) } url := fmt.Sprintf("https://%s/v1/sidecars/%s", c.ControlPlane, d.Id()) if _, err = c.DoRequest(url, http.MethodPut, resourceData); err != nil { - return createError("Unable to update sidecar", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to update sidecar", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] End resourceSidecarUpdate") @@ -307,7 +308,7 @@ func resourceSidecarDelete(ctx context.Context, d *schema.ResourceData, m interf url := fmt.Sprintf("https://%s/v1/sidecars/%s", c.ControlPlane, d.Id()) if _, err := c.DoRequest(url, http.MethodDelete, nil); err != nil { - return createError("Unable to delete sidecar", fmt.Sprintf("%v", err)) + return utils.CreateError("Unable to delete sidecar", fmt.Sprintf("%v", err)) } log.Printf("[DEBUG] End resourceSidecarDelete") diff --git a/cyral/resource_cyral_sidecar_test.go b/cyral/internal/sidecar/resource_cyral_sidecar_test.go similarity index 67% rename from cyral/resource_cyral_sidecar_test.go rename to cyral/internal/sidecar/resource_cyral_sidecar_test.go index 465f06aa..1eca02b2 100644 --- a/cyral/resource_cyral_sidecar_test.go +++ b/cyral/internal/sidecar/resource_cyral_sidecar_test.go @@ -1,19 +1,18 @@ -package cyral +package sidecar_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) -const ( - sidecarResourceName = "sidecar" -) - -func getTestCBS() CertificateBundleSecrets { - cbs := make(CertificateBundleSecrets) - cbs["sidecar"] = &CertificateBundleSecret{ +func getTestCBS() sidecar.CertificateBundleSecrets { + cbs := make(sidecar.CertificateBundleSecrets) + cbs["sidecar"] = &sidecar.CertificateBundleSecret{ SecretId: "someSecret", Type: "aws", Engine: "someEngine", @@ -21,58 +20,58 @@ func getTestCBS() CertificateBundleSecrets { return cbs } -var cloudFormationSidecarConfig = SidecarData{ - Name: accTestName(sidecarResourceName, "cft"), +var cloudFormationSidecarConfig = sidecar.SidecarData{ + Name: utils.AccTestName(utils.SidecarResourceName, "cft"), Labels: []string{"test1"}, - SidecarProperties: NewSidecarProperties("cft-ec2", "foo", ""), + SidecarProperties: sidecar.NewSidecarProperties("cft-ec2", "foo", ""), UserEndpoint: "some.cft.user.endpoint", CertificateBundleSecrets: getTestCBS(), } -var dockerSidecarConfig = SidecarData{ - Name: accTestName(sidecarResourceName, "docker"), +var dockerSidecarConfig = sidecar.SidecarData{ + Name: utils.AccTestName(utils.SidecarResourceName, "docker"), Labels: []string{"test2"}, - SidecarProperties: NewSidecarProperties("docker", "bar", ""), + SidecarProperties: sidecar.NewSidecarProperties("docker", "bar", ""), UserEndpoint: "some.docker.user.endpoint", CertificateBundleSecrets: getTestCBS(), } -var helmSidecarConfig = SidecarData{ - Name: accTestName(sidecarResourceName, "helm3"), +var helmSidecarConfig = sidecar.SidecarData{ + Name: utils.AccTestName(utils.SidecarResourceName, "helm3"), Labels: []string{"test3"}, - SidecarProperties: NewSidecarProperties("helm3", "baz", ""), + SidecarProperties: sidecar.NewSidecarProperties("helm3", "baz", ""), UserEndpoint: "some.helm3.user.endpoint", CertificateBundleSecrets: getTestCBS(), } -var tfSidecarConfig = SidecarData{ - Name: accTestName(sidecarResourceName, "tf"), +var tfSidecarConfig = sidecar.SidecarData{ + Name: utils.AccTestName(utils.SidecarResourceName, "tf"), Labels: []string{"test4"}, - SidecarProperties: NewSidecarProperties("terraform", "qux", ""), + SidecarProperties: sidecar.NewSidecarProperties("terraform", "qux", ""), UserEndpoint: "some.tf.user.endpoint", CertificateBundleSecrets: getTestCBS(), } -var singleContainerSidecarConfig = SidecarData{ - Name: accTestName(sidecarResourceName, "singleContainer"), +var singleContainerSidecarConfig = sidecar.SidecarData{ + Name: utils.AccTestName(utils.SidecarResourceName, "singleContainer"), Labels: []string{"test5"}, - SidecarProperties: NewSidecarProperties("singleContainer", "quxx", ""), + SidecarProperties: sidecar.NewSidecarProperties("singleContainer", "quxx", ""), UserEndpoint: "some.singleContainer.user.endpoint", CertificateBundleSecrets: getTestCBS(), } -var linuxSidecarConfig = SidecarData{ - Name: accTestName(sidecarResourceName, "linux"), +var linuxSidecarConfig = sidecar.SidecarData{ + Name: utils.AccTestName(utils.SidecarResourceName, "linux"), Labels: []string{"test6"}, - SidecarProperties: NewSidecarProperties("linux", "empty", ""), + SidecarProperties: sidecar.NewSidecarProperties("linux", "empty", ""), UserEndpoint: "some.linux.user.endpoint", CertificateBundleSecrets: getTestCBS(), } -var bypassNeverSidecarConfig = SidecarData{ - Name: accTestName(sidecarResourceName, "bypassNeverSidecar"), - SidecarProperties: NewSidecarProperties("terraform", "a", ""), - ServicesConfig: SidecarServicesConfig{ +var bypassNeverSidecarConfig = sidecar.SidecarData{ + Name: utils.AccTestName(utils.SidecarResourceName, "bypassNeverSidecar"), + SidecarProperties: sidecar.NewSidecarProperties("terraform", "a", ""), + ServicesConfig: sidecar.SidecarServicesConfig{ "dispatcher": map[string]string{ "bypass": "never", }, @@ -80,10 +79,10 @@ var bypassNeverSidecarConfig = SidecarData{ UserEndpoint: "some.user.endpoint", } -var bypassAlwaysSidecarConfig = SidecarData{ - Name: accTestName(sidecarResourceName, "bypassAlwaysSidecar"), - SidecarProperties: NewSidecarProperties("terraform", "b", ""), - ServicesConfig: SidecarServicesConfig{ +var bypassAlwaysSidecarConfig = sidecar.SidecarData{ + Name: utils.AccTestName(utils.SidecarResourceName, "bypassAlwaysSidecar"), + SidecarProperties: sidecar.NewSidecarProperties("terraform", "b", ""), + ServicesConfig: sidecar.SidecarServicesConfig{ "dispatcher": map[string]string{ "bypass": "always", }, @@ -107,7 +106,7 @@ func TestAccSidecarResource(t *testing.T) { resource.ParallelTest( t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: []resource.TestStep{ { Config: testConfig, @@ -151,7 +150,7 @@ func TestAccSidecarResource(t *testing.T) { ) } -func setupSidecarTest(sidecarData SidecarData) (string, resource.TestCheckFunc) { +func setupSidecarTest(sidecarData sidecar.SidecarData) (string, resource.TestCheckFunc) { configuration := formatSidecarDataIntoConfig(sidecarData) var deploymentMethod, logIntegrationID string @@ -175,7 +174,7 @@ func setupSidecarTest(sidecarData SidecarData) (string, resource.TestCheckFunc) return configuration, resource.ComposeTestCheckFunc(testFunctions...) } -func formatSidecarDataIntoConfig(sidecarData SidecarData) string { +func formatSidecarDataIntoConfig(sidecarData sidecar.SidecarData) string { var certBundleConfig string if sidecarData.CertificateBundleSecrets != nil { certBundleConfig = fmt.Sprintf( @@ -220,7 +219,7 @@ func formatSidecarDataIntoConfig(sidecarData SidecarData) string { }`, sidecarData.Name, deploymentMethod, logIntegrationID, - listToStr(sidecarData.Labels), + utils.ListToStr(sidecarData.Labels), sidecarData.UserEndpoint, certBundleConfig, servicesConfig, diff --git a/cyral/internal/sweep/sweep.go b/cyral/internal/sweep/sweep.go new file mode 100644 index 00000000..7cf4c9d9 --- /dev/null +++ b/cyral/internal/sweep/sweep.go @@ -0,0 +1 @@ +package sweep diff --git a/cyral/sweep_test.go b/cyral/internal/sweep/sweep_test.go similarity index 72% rename from cyral/sweep_test.go rename to cyral/internal/sweep/sweep_test.go index bc9e7678..424a3d06 100644 --- a/cyral/sweep_test.go +++ b/cyral/internal/sweep/sweep_test.go @@ -1,4 +1,4 @@ -package cyral +package sweep import ( "encoding/json" @@ -6,6 +6,13 @@ import ( "net/http" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/deprecated" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/policy" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/role" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -31,21 +38,21 @@ func TestMain(m *testing.M) { // structure is technically a breaking change, so it might be best to leave this // for the next MAJOR release. -aholmquist 2022-08-10 func init() { - sidecarRepositoryName := sidecarResourceName + "_" + repositoryResourceName + sidecarRepositoryName := utils.SidecarResourceName + "_" + utils.RepositoryResourceName resource.AddTestSweepers(sidecarRepositoryName, &resource.Sweeper{ Name: sidecarRepositoryName, F: sweepSidecarAndRepository, }) - resource.AddTestSweepers(roleResourceName, &resource.Sweeper{ - Name: roleResourceName, + resource.AddTestSweepers(utils.RoleResourceName, &resource.Sweeper{ + Name: utils.RoleResourceName, F: sweepRole, }) - resource.AddTestSweepers(integrationIdPResourceName, &resource.Sweeper{ - Name: integrationIdPResourceName, + resource.AddTestSweepers(utils.IntegrationIdPResourceName, &resource.Sweeper{ + Name: utils.IntegrationIdPResourceName, F: sweepIntegrationIdP, }) - resource.AddTestSweepers(policyResourceName, &resource.Sweeper{ - Name: policyResourceName, + resource.AddTestSweepers(utils.PolicyResourceName, &resource.Sweeper{ + Name: utils.PolicyResourceName, F: sweepPolicy, }) // TODO: add sweepers for rest of resources -aholmquist 2022-08-10 @@ -67,17 +74,17 @@ func sweepSidecarAndRepository(_ string) error { } func sweepRepository(_ string) error { - c, err := newClientFromEnv() + c, err := client.FromEnv() if err != nil { return err } url := fmt.Sprintf("https://%s/v1/repos?name=^%s", c.ControlPlane, - tprovACCPrefix) + utils.TFProvACCPrefix) reposBytes, err := c.DoRequest(url, http.MethodGet, nil) if err != nil { return fmt.Errorf("get request returned error: %w", err) } - repos := GetReposResponse{} + repos := repository.GetReposResponse{} if err := json.Unmarshal(reposBytes, &repos); err != nil { return fmt.Errorf("error unmarshaling resp: %w", err) } @@ -92,16 +99,16 @@ func sweepRepository(_ string) error { } func sweepSidecar(_ string) error { - c, err := newClientFromEnv() + c, err := client.FromEnv() if err != nil { return err } - sidecars, err := listSidecars(c) + sidecars, err := sidecar.ListSidecars(c) if err != nil { return err } for _, sidecar := range sidecars { - if !hasAccTestPrefix(sidecar.Sidecar.Name) { + if !utils.HasAccTestPrefix(sidecar.Sidecar.Name) { continue } url := fmt.Sprintf("https://%s/v1/sidecars/%s", c.ControlPlane, @@ -115,17 +122,17 @@ func sweepSidecar(_ string) error { } func sweepRole(_ string) error { - c, err := newClientFromEnv() + c, err := client.FromEnv() if err != nil { return err } - resp, err := listRoles(c) + resp, err := role.ListRoles(c) if err != nil { return err } roles := resp.Groups for _, role := range roles { - if !hasAccTestPrefix(role.Name) { + if !utils.HasAccTestPrefix(role.Name) { continue } url := fmt.Sprintf("https://%s/v1/users/groups/%s", c.ControlPlane, @@ -139,16 +146,16 @@ func sweepRole(_ string) error { } func sweepPolicy(_ string) error { - c, err := newClientFromEnv() + c, err := client.FromEnv() if err != nil { return err } - policies, err := listPolicies(c) + policies, err := policy.ListPolicies(c) if err != nil { return err } for _, policy := range policies { - if !hasAccTestPrefix(policy.Meta.Name) { + if !utils.HasAccTestPrefix(policy.Meta.Name) { continue } url := fmt.Sprintf("https://%s/v1/policies/%s", @@ -162,18 +169,18 @@ func sweepPolicy(_ string) error { } func sweepIntegrationIdP(_ string) error { - c, err := newClientFromEnv() + c, err := client.FromEnv() if err != nil { return err } - resp, err := listIdPIntegrations(c) + resp, err := deprecated.ListIdPIntegrations(c) if err != nil { return fmt.Errorf("failed to get IdP integrations: %w", err) } integrations := resp.Connections.Connections for _, integration := range integrations { - if !hasAccTestPrefix(integration.DisplayName) { + if !utils.HasAccTestPrefix(integration.DisplayName) { continue } url := fmt.Sprintf("https://%s/v1/integrations/saml/%s", diff --git a/cyral/data_source_cyral_system_info.go b/cyral/internal/systeminfo/data_source_cyral_system_info.go similarity index 78% rename from cyral/data_source_cyral_system_info.go rename to cyral/internal/systeminfo/data_source_cyral_system_info.go index 873e6090..296eac48 100644 --- a/cyral/data_source_cyral_system_info.go +++ b/cyral/internal/systeminfo/data_source_cyral_system_info.go @@ -1,10 +1,12 @@ -package cyral +package systeminfo import ( "fmt" "net/http" - "github.com/cyralinc/terraform-provider-cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -28,21 +30,21 @@ func (systemInfo *SystemInfo) WriteToSchema(d *schema.ResourceData) error { return nil } -func dataSourceSystemInfo() *schema.Resource { +func DataSourceSystemInfo() *schema.Resource { return &schema.Resource{ Description: "Retrieve information from Cyral system.", - ReadContext: ReadResource(ResourceOperationConfig{ + ReadContext: core.ReadResource(core.ResourceOperationConfig{ Name: "SystemInfoDataSourceRead", HttpMethod: http.MethodGet, CreateURL: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/systemInfo", c.ControlPlane) }, - NewResponseData: func(_ *schema.ResourceData) ResponseData { + NewResponseData: func(_ *schema.ResourceData) core.ResponseData { return &SystemInfo{} }, }), Schema: map[string]*schema.Schema{ - IDKey: { + utils.IDKey: { Description: "Data source identifier.", Type: schema.TypeString, Computed: true, diff --git a/cyral/data_source_cyral_system_info_test.go b/cyral/internal/systeminfo/data_source_cyral_system_info_test.go similarity index 73% rename from cyral/data_source_cyral_system_info_test.go rename to cyral/internal/systeminfo/data_source_cyral_system_info_test.go index 1b6502bf..e0c481c8 100644 --- a/cyral/data_source_cyral_system_info_test.go +++ b/cyral/internal/systeminfo/data_source_cyral_system_info_test.go @@ -1,9 +1,12 @@ -package cyral +package systeminfo_test import ( "fmt" "testing" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/systeminfo" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -17,7 +20,7 @@ func TestAccSystemInfoDataSource(t *testing.T) { accTestStepSystemInfoDataSource_ListAllSystemInfo(dataSourceName), } resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: providerFactories, + ProviderFactories: provider.ProviderFactories, Steps: testSteps, }) } @@ -31,15 +34,15 @@ func accTestStepSystemInfoDataSource_ListAllSystemInfo(dataSourceName string) re check := resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet( dataSourceFullName, - IDKey, + utils.IDKey, ), resource.TestCheckResourceAttrSet( dataSourceFullName, - ControlPlaneVersionKey, + systeminfo.ControlPlaneVersionKey, ), resource.TestCheckResourceAttrSet( dataSourceFullName, - SidecarLatestVersionKey, + systeminfo.SidecarLatestVersionKey, ), ) return resource.TestStep{ diff --git a/cyral/model_datalabel.go b/cyral/model_datalabel.go deleted file mode 100644 index 41830140..00000000 --- a/cyral/model_datalabel.go +++ /dev/null @@ -1,69 +0,0 @@ -package cyral - -const ( - typeUnknown = "UNKNOWN" - statusEnabled = "ENABLED" - statusDisabled = "DISABLED" - // Data label type - dataLabelTypePredefined = "PREDEFINED" - dataLabelTypeCustom = "CUSTOM" - defaultDataLabelType = typeUnknown - // Classification rule type - classificationRuleTypeRego = "REGO" -) - -func dataLabelTypes() []string { - return []string{ - typeUnknown, - dataLabelTypePredefined, - dataLabelTypeCustom, - } -} - -func classificationRuleTypes() []string { - return []string{ - typeUnknown, - classificationRuleTypeRego, - } -} - -func classificationRuleStatus() []string { - return []string{ - statusEnabled, - statusDisabled, - } -} - -type DataLabel struct { - Name string `json:"name,omitempty"` - Type string `json:"type,omitempty"` - Description string `json:"description,omitempty"` - Tags []string `json:"tags,omitempty"` - ClassificationRule *DataLabelClassificationRule `json:"classificationRule,omitempty"` - Implicit bool `json:"implicit,omitempty"` -} - -func (dl *DataLabel) TagsAsInterface() []interface{} { - var tagIfaces []interface{} - for _, tag := range dl.Tags { - tagIfaces = append(tagIfaces, tag) - } - return tagIfaces -} - -func (dl *DataLabel) ClassificationRuleAsInterface() []interface{} { - if dl.ClassificationRule == nil { - return nil - } - return []interface{}{map[string]interface{}{ - "rule_type": dl.ClassificationRule.RuleType, - "rule_code": dl.ClassificationRule.RuleCode, - "rule_status": dl.ClassificationRule.RuleStatus, - }} -} - -type DataLabelClassificationRule struct { - RuleType string `json:"ruleType"` - RuleCode string `json:"ruleCode"` - RuleStatus string `json:"status"` -} diff --git a/cyral/provider.go b/cyral/provider.go deleted file mode 100644 index 2b9b03c0..00000000 --- a/cyral/provider.go +++ /dev/null @@ -1,234 +0,0 @@ -package cyral - -import ( - "context" - "fmt" - "log" - "os" - "strconv" - "strings" - - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - - "github.com/cyralinc/terraform-provider-cyral/client" -) - -const ( - EnvVarClientID = "CYRAL_TF_CLIENT_ID" - EnvVarClientSecret = "CYRAL_TF_CLIENT_SECRET" - EnvVarCPURL = "CYRAL_TF_CONTROL_PLANE" - EnvVarTLSSkipVerify = "CYRAL_TF_TLS_SKIP_VERIFY" -) - -func init() { - schema.ResourceDescriptionBuilder = func(s *schema.Resource) string { - desc := s.Description - if s.DeprecationMessage != "" { - desc = fmt.Sprintf("\n~> **DEPRECATED** %s", s.DeprecationMessage) - } - return strings.TrimSpace(desc) - } -} - -// Provider defines and initializes the Cyral provider -func Provider() *schema.Provider { - var idpDeprecationMessage = "Use resource and data source `cyral_integration_idp_saml` instead." - return &schema.Provider{ - Schema: map[string]*schema.Schema{ - "client_id": { - Description: "Client id used to authenticate against the control plane. Can be ommited and " + - "declared using the environment variable `CYRAL_TF_CLIENT_ID`.", - Type: schema.TypeString, - Optional: true, - Sensitive: true, - DefaultFunc: schema.EnvDefaultFunc(EnvVarClientID, nil), - }, - "client_secret": { - Description: "Client secret used to authenticate against the control plane. Can be ommited and " + - "declared using the environment variable `CYRAL_TF_CLIENT_SECRET`.", - Type: schema.TypeString, - Optional: true, - Sensitive: true, - DefaultFunc: schema.EnvDefaultFunc(EnvVarClientSecret, nil), - }, - "control_plane": { - Description: "Control plane host and API port (ex: `tenant.app.cyral.com`)", - Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc(EnvVarCPURL, nil), - }, - "tls_skip_verify": { - Type: schema.TypeBool, - Description: "Specifies if the client will verify the TLS server certificate " + - "used by the control plane. If set to `true`, the client will not verify " + - "the server certificate, hence, it will allow insecure connections to be " + - "established. This should be set only for testing and is not recommended " + - "to be used in production environments. Can be set through the " + - "`CYRAL_TF_TLS_SKIP_VERIFY` environment variable. Defaults to `false`.", - Optional: true, - DefaultFunc: schema.EnvDefaultFunc(EnvVarTLSSkipVerify, nil), - }, - }, - DataSourcesMap: map[string]*schema.Resource{ - "cyral_datalabel": dataSourceDatalabel(), - "cyral_integration_idp": dataSourceIntegrationIdP(), - "cyral_integration_idp_saml": dataSourceIntegrationIdPSAML(), - "cyral_integration_logging": dataSourceIntegrationLogging(), - "cyral_permission": dataSourcePermission(), - "cyral_repository": dataSourceRepository(), - "cyral_role": dataSourceRole(), - "cyral_saml_certificate": dataSourceSAMLCertificate(), - "cyral_saml_configuration": dataSourceSAMLConfiguration(), - "cyral_sidecar_bound_ports": dataSourceSidecarBoundPorts(), - "cyral_sidecar_cft_template": dataSourceSidecarCftTemplate(), - "cyral_sidecar_health": dataSourceSidecarHealth(), - "cyral_sidecar_id": dataSourceSidecarID(), - "cyral_sidecar_instance_ids": dataSourceSidecarInstanceIDs(), - "cyral_sidecar_instance_stats": dataSourceSidecarInstanceStats(), - "cyral_sidecar_instance": dataSourceSidecarInstance(), - "cyral_sidecar_listener": dataSourceSidecarListener(), - "cyral_system_info": dataSourceSystemInfo(), - }, - - ResourcesMap: map[string]*schema.Resource{ - "cyral_datalabel": resourceDatalabel(), - "cyral_integration_aws_iam": resourceIntegrationAWSIAM(), - "cyral_integration_datadog": resourceIntegrationDatadog(), - "cyral_integration_mfa_duo": resourceIntegrationMFADuo(), - "cyral_integration_elk": resourceIntegrationELK(), - "cyral_integration_hc_vault": resourceIntegrationHCVault(), - "cyral_integration_logstash": resourceIntegrationLogstash(), - "cyral_integration_looker": resourceIntegrationLooker(), - "cyral_integration_microsoft_teams": resourceIntegrationMsTeams(), - "cyral_integration_pager_duty": resourceIntegrationPagerDuty(), - "cyral_integration_slack_alerts": resourceIntegrationSlackAlerts(), - "cyral_integration_splunk": resourceIntegrationSplunk(), - "cyral_integration_idp_aad": resourceIntegrationIdP("aad", idpDeprecationMessage), - "cyral_integration_idp_adfs": resourceIntegrationIdP("adfs-2016", idpDeprecationMessage), - "cyral_integration_idp_forgerock": resourceIntegrationIdP("forgerock", ""), - "cyral_integration_idp_gsuite": resourceIntegrationIdP("gsuite", idpDeprecationMessage), - "cyral_integration_idp_okta": resourceIntegrationIdP("okta", idpDeprecationMessage), - "cyral_integration_idp_ping_one": resourceIntegrationIdP("pingone", idpDeprecationMessage), - "cyral_integration_idp_saml": resourceIntegrationIdPSAML(), - "cyral_integration_idp_saml_draft": resourceIntegrationIdPSAMLDraft(), - "cyral_integration_sumo_logic": resourceIntegrationSumoLogic(), - "cyral_integration_logging": resourceIntegrationLogging(), - "cyral_policy": resourcePolicy(), - "cyral_policy_rule": resourcePolicyRule(), - "cyral_rego_policy_instance": resourceRegoPolicyInstance(), - "cyral_repository": resourceRepository(), - "cyral_repository_binding": resourceRepositoryBinding(), - "cyral_repository_conf_analysis": resourceRepositoryConfAnalysis(), - "cyral_repository_conf_auth": resourceRepositoryConfAuth(), - "cyral_repository_datamap": resourceRepositoryDatamap(), - "cyral_repository_user_account": resourceRepositoryUserAccount(), - "cyral_repository_network_access_policy": resourceRepositoryNetworkAccessPolicy(), - "cyral_repository_access_rules": resourceRepositoryAccessRules(), - "cyral_repository_access_gateway": resourceRepositoryAccessGateway(), - "cyral_role": resourceRole(), - "cyral_role_sso_groups": resourceRoleSSOGroups(), - "cyral_service_account": resourceServiceAccount(), - "cyral_sidecar": resourceSidecar(), - "cyral_sidecar_credentials": resourceSidecarCredentials(), - "cyral_sidecar_listener": resourceSidecarListener(), - }, - ConfigureContextFunc: providerConfigure, - } -} - -func providerConfigure(ctx context.Context, d *schema.ResourceData) (any, diag.Diagnostics) { - log.Printf("[DEBUG] Init providerConfigure") - - clientID, clientSecret, diags := getCredentials(d) - if diags.HasError() { - return nil, diags - } - - controlPlane := d.Get("control_plane").(string) - tlsSkipVerify := d.Get("tls_skip_verify").(bool) - log.Printf("[DEBUG] controlPlane: %s ; tlsSkipVerify: %t", controlPlane, tlsSkipVerify) - - c, err := client.NewClient(clientID, clientSecret, controlPlane, tlsSkipVerify) - if err != nil { - diags = append(diags, diag.Diagnostic{ - Severity: diag.Error, - Summary: "Unable to create Cyral client", - Detail: err.Error(), - }) - - return nil, diags - } - log.Printf("[DEBUG] End providerConfigure") - - return c, diags -} - -func getCredentials(d *schema.ResourceData) (string, string, diag.Diagnostics) { - var clientID, clientSecret string - - getVar := func(providerVar, envVar string, diags *diag.Diagnostics) string { - value := d.Get(providerVar).(string) - if value == "" { - (*diags) = append((*diags), diag.Diagnostic{ - Severity: diag.Error, - Summary: "Unable to read credentials", - Detail: fmt.Sprintf("use provider variable '%s' or environment variable '%s'", providerVar, envVar), - }) - } - return value - } - var diags diag.Diagnostics - - clientID = getVar("client_id", EnvVarClientID, &diags) - clientSecret = getVar("client_secret", EnvVarClientSecret, &diags) - - return clientID, clientSecret, diags -} - -func getProviderConfigFromEnv() ( - clientID string, - clientSecret string, - controlPlane string, - tlsSkipVerify bool, - err error, -) { - clientID = os.Getenv(EnvVarClientID) - clientSecret = os.Getenv(EnvVarClientSecret) - controlPlane = os.Getenv(EnvVarCPURL) - tlsSkipVerifyStr := os.Getenv(EnvVarTLSSkipVerify) - if tlsSkipVerifyStr != "" { - tlsSkipVerify, err = strconv.ParseBool(tlsSkipVerifyStr) - if err != nil { - return "", "", "", false, fmt.Errorf("invalid value for "+ - "env var %q: %w", EnvVarTLSSkipVerify, err) - } - } - return -} - -func newClientFromEnv() (*client.Client, error) { - clientID, clientSecret, controlPlane, tlsSkipVerify, err := - getProviderConfigFromEnv() - if err != nil { - return nil, fmt.Errorf("unable to create Cyral client: %w", err) - } - c, err := client.NewClient(clientID, clientSecret, controlPlane, - tlsSkipVerify) - if err != nil { - return nil, fmt.Errorf("unable to create Cyral client: %w", err) - } - return c, nil -} - -func createError(summary, detail string) diag.Diagnostics { - var diags diag.Diagnostics - - diags = append(diags, diag.Diagnostic{ - Severity: diag.Error, - Summary: summary, - Detail: detail, - }) - - return diags -} diff --git a/cyral/provider/provider.go b/cyral/provider/provider.go new file mode 100644 index 00000000..fc9cfc8e --- /dev/null +++ b/cyral/provider/provider.go @@ -0,0 +1,251 @@ +package provider + +import ( + "context" + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/deprecated" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/awsiam" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/confextension/mfaduo" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/confextension/pagerduty" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/hcvault" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/idpsaml" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/logging" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/slack" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/teams" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/permission" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/policy" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/policy/rule" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/regopolicy" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/accessgateway" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/accessrules" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/binding" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/confanalysis" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/confauth" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/network" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/useraccount" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/role" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/samlcertificate" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/samlconfiguration" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/serviceaccount" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/credentials" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/health" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/instance" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/listener" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/systeminfo" +) + +func init() { + schema.ResourceDescriptionBuilder = func(s *schema.Resource) string { + desc := s.Description + if s.DeprecationMessage != "" { + desc = fmt.Sprintf("\n~> **DEPRECATED** %s", s.DeprecationMessage) + } + return strings.TrimSpace(desc) + } +} + +// Provider defines and initializes the Cyral provider +func Provider() *schema.Provider { + ps := packagesSchemas() + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + "client_id": { + Description: "Client id used to authenticate against the control plane. Can be ommited and " + + "declared using the environment variable `CYRAL_TF_CLIENT_ID`.", + Type: schema.TypeString, + Optional: true, + Sensitive: true, + DefaultFunc: schema.EnvDefaultFunc(client.EnvVarClientID, nil), + }, + "client_secret": { + Description: "Client secret used to authenticate against the control plane. Can be ommited and " + + "declared using the environment variable `CYRAL_TF_CLIENT_SECRET`.", + Type: schema.TypeString, + Optional: true, + Sensitive: true, + DefaultFunc: schema.EnvDefaultFunc(client.EnvVarClientSecret, nil), + }, + "control_plane": { + Description: "Control plane host and API port (ex: `tenant.app.cyral.com`)", + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc(client.EnvVarCPURL, nil), + }, + "tls_skip_verify": { + Type: schema.TypeBool, + Description: "Specifies if the client will verify the TLS server certificate " + + "used by the control plane. If set to `true`, the client will not verify " + + "the server certificate, hence, it will allow insecure connections to be " + + "established. This should be set only for testing and is not recommended " + + "to be used in production environments. Can be set through the " + + "`CYRAL_TF_TLS_SKIP_VERIFY` environment variable. Defaults to `false`.", + Optional: true, + DefaultFunc: schema.EnvDefaultFunc(client.EnvVarTLSSkipVerify, nil), + }, + }, + DataSourcesMap: getDataSourceMap(ps), + ResourcesMap: getResourceMap(ps), + ConfigureContextFunc: providerConfigure, + } +} + +func getDataSourceMap(ps []core.PackageSchema) map[string]*schema.Resource { + log.Printf("[DEBUG] Init getDataSourceMap") + schemaMap := map[string]*schema.Resource{} + for _, p := range ps { + log.Printf("[DEBUG] Looking for datasources in package `%s`", p.Name()) + for _, v := range p.Schemas() { + if v.Type == core.DataSourceSchemaType { + log.Printf("[DEBUG] Registering datasources `%s`", v.Name) + schemaMap[v.Name] = v.Schema() + } + } + } + + schemaMap["cyral_integration_idp"] = deprecated.DataSourceIntegrationIdP() + schemaMap["cyral_integration_idp_saml"] = idpsaml.DataSourceIntegrationIdPSAML() + schemaMap["cyral_integration_logging"] = logging.DataSourceIntegrationLogging() + schemaMap["cyral_permission"] = permission.DataSourcePermission() + schemaMap["cyral_repository"] = repository.DataSourceRepository() + schemaMap["cyral_role"] = role.DataSourceRole() + schemaMap["cyral_saml_certificate"] = samlcertificate.DataSourceSAMLCertificate() + schemaMap["cyral_saml_configuration"] = samlconfiguration.DataSourceSAMLConfiguration() + schemaMap["cyral_sidecar_bound_ports"] = sidecar.DataSourceSidecarBoundPorts() + schemaMap["cyral_sidecar_cft_template"] = deprecated.DataSourceSidecarCftTemplate() + schemaMap["cyral_sidecar_health"] = health.DataSourceSidecarHealth() + schemaMap["cyral_sidecar_id"] = sidecar.DataSourceSidecarID() + schemaMap["cyral_sidecar_instance_ids"] = deprecated.DataSourceSidecarInstanceIDs() + schemaMap["cyral_sidecar_instance_stats"] = instance.DataSourceSidecarInstanceStats() + schemaMap["cyral_sidecar_instance"] = instance.DataSourceSidecarInstance() + schemaMap["cyral_sidecar_listener"] = listener.DataSourceSidecarListener() + schemaMap["cyral_system_info"] = systeminfo.DataSourceSystemInfo() + + log.Printf("[DEBUG] end getDataSourceMap") + + return schemaMap +} + +func getResourceMap(ps []core.PackageSchema) map[string]*schema.Resource { + log.Printf("[DEBUG] Init getResourceMap") + var idpDeprecationMessage = "Use resource and data source `cyral_integration_idp_saml` instead." + schemaMap := map[string]*schema.Resource{} + for _, p := range ps { + log.Printf("[DEBUG] Looking for resources in package `%s`", p.Name()) + for _, v := range p.Schemas() { + if v.Type == core.ResourceSchemaType { + log.Printf("[DEBUG] Registering resources `%s`", v.Name) + schemaMap[v.Name] = v.Schema() + } + } + } + + // // TODO Once the resources are migrated to the new SchemaRegister + // // abstraction, these calls from provider to resource will be removed. + schemaMap["cyral_integration_aws_iam"] = awsiam.ResourceIntegrationAWSIAM() + schemaMap["cyral_integration_datadog"] = deprecated.ResourceIntegrationDatadog() + schemaMap["cyral_integration_mfa_duo"] = mfaduo.ResourceIntegrationMFADuo() + schemaMap["cyral_integration_elk"] = deprecated.ResourceIntegrationELK() + schemaMap["cyral_integration_hc_vault"] = hcvault.ResourceIntegrationHCVault() + schemaMap["cyral_integration_logstash"] = deprecated.ResourceIntegrationLogstash() + schemaMap["cyral_integration_looker"] = deprecated.ResourceIntegrationLooker() + schemaMap["cyral_integration_microsoft_teams"] = teams.ResourceIntegrationMsTeams() + schemaMap["cyral_integration_pager_duty"] = pagerduty.ResourceIntegrationPagerDuty() + schemaMap["cyral_integration_slack_alerts"] = slack.ResourceIntegrationSlackAlerts() + schemaMap["cyral_integration_splunk"] = deprecated.ResourceIntegrationSplunk() + schemaMap["cyral_integration_idp_aad"] = deprecated.ResourceIntegrationIdP("aad", idpDeprecationMessage) + schemaMap["cyral_integration_idp_adfs"] = deprecated.ResourceIntegrationIdP("adfs-2016", idpDeprecationMessage) + schemaMap["cyral_integration_idp_forgerock"] = deprecated.ResourceIntegrationIdP("forgerock", "") + schemaMap["cyral_integration_idp_gsuite"] = deprecated.ResourceIntegrationIdP("gsuite", idpDeprecationMessage) + schemaMap["cyral_integration_idp_okta"] = deprecated.ResourceIntegrationIdP("okta", idpDeprecationMessage) + schemaMap["cyral_integration_idp_ping_one"] = deprecated.ResourceIntegrationIdP("pingone", idpDeprecationMessage) + schemaMap["cyral_integration_idp_saml"] = idpsaml.ResourceIntegrationIdPSAML() + schemaMap["cyral_integration_idp_saml_draft"] = idpsaml.ResourceIntegrationIdPSAMLDraft() + schemaMap["cyral_integration_sumo_logic"] = deprecated.ResourceIntegrationSumoLogic() + schemaMap["cyral_integration_logging"] = logging.ResourceIntegrationLogging() + schemaMap["cyral_policy"] = policy.ResourcePolicy() + schemaMap["cyral_policy_rule"] = rule.ResourcePolicyRule() + schemaMap["cyral_rego_policy_instance"] = regopolicy.ResourceRegoPolicyInstance() + schemaMap["cyral_repository"] = repository.ResourceRepository() + schemaMap["cyral_repository_access_rules"] = accessrules.ResourceRepositoryAccessRules() + schemaMap["cyral_repository_access_gateway"] = accessgateway.ResourceRepositoryAccessGateway() + schemaMap["cyral_repository_binding"] = binding.ResourceRepositoryBinding() + schemaMap["cyral_repository_conf_auth"] = confauth.ResourceRepositoryConfAuth() + schemaMap["cyral_repository_conf_analysis"] = confanalysis.ResourceRepositoryConfAnalysis() + schemaMap["cyral_repository_network_access_policy"] = network.ResourceRepositoryNetworkAccessPolicy() + schemaMap["cyral_repository_user_account"] = useraccount.ResourceRepositoryUserAccount() + schemaMap["cyral_role"] = role.ResourceRole() + schemaMap["cyral_role_sso_groups"] = role.ResourceRoleSSOGroups() + schemaMap["cyral_service_account"] = serviceaccount.ResourceServiceAccount() + schemaMap["cyral_sidecar"] = sidecar.ResourceSidecar() + schemaMap["cyral_sidecar_credentials"] = credentials.ResourceSidecarCredentials() + schemaMap["cyral_sidecar_listener"] = listener.ResourceSidecarListener() + + log.Printf("[DEBUG] End getResourceMap") + + return schemaMap +} + +func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { + log.Printf("[DEBUG] Init providerConfigure") + + clientID, clientSecret, diags := getCredentials(d) + if diags.HasError() { + return nil, diags + } + + controlPlane := d.Get("control_plane").(string) + tlsSkipVerify := d.Get("tls_skip_verify").(bool) + log.Printf("[DEBUG] controlPlane: %s ; tlsSkipVerify: %t", controlPlane, tlsSkipVerify) + + c, err := client.New(clientID, clientSecret, controlPlane, tlsSkipVerify) + if err != nil { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to create Cyral client", + Detail: err.Error(), + }) + + return nil, diags + } + log.Printf("[DEBUG] End providerConfigure") + + return c, diags +} + +func getCredentials(d *schema.ResourceData) (string, string, diag.Diagnostics) { + var clientID, clientSecret string + + getVar := func(providerVar, envVar string, diags *diag.Diagnostics) string { + value := d.Get(providerVar).(string) + if value == "" { + (*diags) = append((*diags), diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to read credentials", + Detail: fmt.Sprintf("use provider variable '%s' or environment variable '%s'", providerVar, envVar), + }) + } + return value + } + var diags diag.Diagnostics + + clientID = getVar("client_id", client.EnvVarClientID, &diags) + clientSecret = getVar("client_secret", client.EnvVarClientSecret, &diags) + + return clientID, clientSecret, diags +} + +var ProviderFactories = map[string]func() (*schema.Provider, error){ + "cyral": func() (*schema.Provider, error) { + return Provider(), nil + }, +} diff --git a/cyral/provider/provider_test.go b/cyral/provider/provider_test.go new file mode 100644 index 00000000..87e30d52 --- /dev/null +++ b/cyral/provider/provider_test.go @@ -0,0 +1,15 @@ +package provider + +import ( + "testing" +) + +const ( + EnvVarTFAcc = "TF_ACC" +) + +func TestAccProvider(t *testing.T) { + if err := Provider().InternalValidate(); err != nil { + t.Fatalf("err: %s", err) + } +} diff --git a/cyral/provider/schema_loader.go b/cyral/provider/schema_loader.go new file mode 100644 index 00000000..7725fa3a --- /dev/null +++ b/cyral/provider/schema_loader.go @@ -0,0 +1,15 @@ +package provider + +import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/datalabel" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/datamap" +) + +func packagesSchemas() []core.PackageSchema { + v := []core.PackageSchema{ + datalabel.PackageSchema(), + datamap.PackageSchema(), + } + return v +} diff --git a/cyral/provider_test.go b/cyral/provider_test.go deleted file mode 100644 index 86f49b18..00000000 --- a/cyral/provider_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package cyral - -import ( - "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -const ( - EnvVarTFAcc = "TF_ACC" -) - -func TestAccProvider(t *testing.T) { - if err := Provider().InternalValidate(); err != nil { - t.Fatalf("err: %s", err) - } -} - -var providerFactories = map[string]func() (*schema.Provider, error){ - "cyral": func() (*schema.Provider, error) { - return Provider(), nil - }, -} diff --git a/cyral/resource_cyral_datalabel.go b/cyral/resource_cyral_datalabel.go deleted file mode 100644 index ce97a717..00000000 --- a/cyral/resource_cyral_datalabel.go +++ /dev/null @@ -1,219 +0,0 @@ -package cyral - -import ( - "context" - "encoding/json" - "fmt" - "log" - "net/http" - - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - - "github.com/cyralinc/terraform-provider-cyral/client" -) - -func resourceDatalabel() *schema.Resource { - return &schema.Resource{ - Description: "Manages data labels. Data labels are part of the Cyral [Data Map](https://cyral.com/docs/policy/datamap).", - CreateContext: resourceDatalabelCreate, - ReadContext: resourceDatalabelRead, - UpdateContext: resourceDatalabelUpdate, - DeleteContext: resourceDatalabelDelete, - Schema: map[string]*schema.Schema{ - "name": { - Description: "Name of the data label.", - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "description": { - Description: "Description of the data label.", - Type: schema.TypeString, - Optional: true, - }, - "tags": { - Description: "Tags that can be used to categorize data labels.", - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "classification_rule": { - Description: "Classification rules are used by the " + - "[Automatic Data Map](https://cyral.com/docs/policy/automatic-datamap) feature to automatically map " + - "data locations to labels.", - Optional: true, - Type: schema.TypeSet, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "rule_type": { - Description: "Type of the classification rule. Valid values are: `UNKNOWN` and `REGO`. Defaults " + - "to `UNKNOWN`.", - Type: schema.TypeString, - Optional: true, - Default: "UNKNOWN", - ValidateFunc: validation.StringInSlice(classificationRuleTypes(), false), - }, - "rule_code": { - Description: "Actual code of the classification rule. For example, this attribute may contain " + - "REGO code for `REGO`-type classification rules.", - Type: schema.TypeString, - Optional: true, - }, - "rule_status": { - Description: "Status of the classification rule. Valid values are: `ENABLED` and `DISABLED`. " + - "Defaults to `ENABLED`.", - Type: schema.TypeString, - Optional: true, - Default: "ENABLED", - ValidateFunc: validation.StringInSlice(classificationRuleStatus(), false), - }, - }, - }, - }, - }, - Importer: &schema.ResourceImporter{ - StateContext: func( - ctx context.Context, - d *schema.ResourceData, - m any, - ) ([]*schema.ResourceData, error) { - d.Set("name", d.Id()) - return []*schema.ResourceData{d}, nil - }, - }, - } -} - -func resourceDatalabelCreate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { - log.Printf("[DEBUG] Init resourceDatalabelCreate") - c := m.(*client.Client) - - labelName := d.Get("name").(string) - url := fmt.Sprintf("https://%s/v1/datalabels/%s", c.ControlPlane, labelName) - - dataLabel := getDataLabelFromResource(d) - - _, err := c.DoRequest(url, http.MethodPut, dataLabel) - if err != nil { - return createError("Unable to create data label", err.Error()) - } - - d.SetId(labelName) - - log.Printf("[DEBUG] End resourceDatalabelCreate") - - return resourceDatalabelRead(ctx, d, m) -} - -func resourceDatalabelRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { - log.Printf("[DEBUG] Init resourceDatalabelRead") - c := m.(*client.Client) - - labelName := d.Get("name").(string) - url := fmt.Sprintf("https://%s/v1/datalabels/%s", c.ControlPlane, labelName) - - body, err := c.DoRequest(url, http.MethodGet, nil) - if err != nil { - return createError("Unable to create data label", err.Error()) - } - - dataLabel := DataLabel{} - if err := json.Unmarshal(body, &dataLabel); err != nil { - return createError("Unable to unmarshall JSON", err.Error()) - } - log.Printf("[DEBUG] Response body (unmarshalled): %#v", dataLabel) - - if err := writeDataLabelToResourceSchema(dataLabel, d); err != nil { - return createError("Unable to read data label", err.Error()) - } - - log.Printf("[DEBUG] End resourceDatalabelRead") - - return diag.Diagnostics{} -} - -func resourceDatalabelUpdate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { - log.Printf("[DEBUG] Init resourceDatalabelUpdate") - c := m.(*client.Client) - - dataLabel := getDataLabelFromResource(d) - - labelName := d.Get("name").(string) - url := fmt.Sprintf("https://%s/v1/datalabels/%s", c.ControlPlane, labelName) - - _, err := c.DoRequest(url, http.MethodPut, dataLabel) - if err != nil { - return createError("Unable to create data label", err.Error()) - } - - log.Printf("[DEBUG] End resourceDatalabelUpdate") - - return resourceDatalabelRead(ctx, d, m) -} - -func resourceDatalabelDelete(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { - log.Printf("[DEBUG] Init resourceDatalabelDelete") - c := m.(*client.Client) - - labelName := d.Get("name").(string) - url := fmt.Sprintf("https://%s/v1/datalabels/%s", c.ControlPlane, labelName) - - _, err := c.DoRequest(url, http.MethodDelete, nil) - if err != nil { - return createError("Unable to delete data label", err.Error()) - } - - log.Printf("[DEBUG] End resourceDatalabelDelete") - - return diag.Diagnostics{} -} - -func getDataLabelFromResource(d *schema.ResourceData) DataLabel { - var tags []string - tagIfaces := d.Get("tags").([]any) - for _, tagIface := range tagIfaces { - tags = append(tags, tagIface.(string)) - } - - var classificationRule *DataLabelClassificationRule - classificationRuleList := d.Get("classification_rule").(*schema.Set).List() - if len(classificationRuleList) > 0 { - classificationRuleMap := classificationRuleList[0].(map[string]any) - classificationRule = &DataLabelClassificationRule{ - RuleType: classificationRuleMap["rule_type"].(string), - RuleCode: classificationRuleMap["rule_code"].(string), - RuleStatus: classificationRuleMap["rule_status"].(string), - } - } - - return DataLabel{ - Name: d.Get("name").(string), - Type: dataLabelTypeCustom, - Description: d.Get("description").(string), - Tags: tags, - ClassificationRule: classificationRule, - } -} - -func writeDataLabelToResourceSchema(label DataLabel, d *schema.ResourceData) error { - if err := d.Set("description", label.Description); err != nil { - return fmt.Errorf("error setting 'description' field: %w", err) - } - - tagIfaces := label.TagsAsInterface() - if err := d.Set("tags", tagIfaces); err != nil { - return fmt.Errorf("error setting 'tags' field: %w", err) - } - - classificationRule := label.ClassificationRuleAsInterface() - if err := d.Set("classification_rule", classificationRule); err != nil { - return fmt.Errorf("error setting 'classification_rule' field: %w", err) - } - - return nil -} diff --git a/cyral/resource_cyral_repository_datamap.go b/cyral/resource_cyral_repository_datamap.go deleted file mode 100644 index 2f939763..00000000 --- a/cyral/resource_cyral_repository_datamap.go +++ /dev/null @@ -1,246 +0,0 @@ -package cyral - -import ( - "context" - "encoding/json" - "fmt" - "log" - "net/http" - - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - - "github.com/cyralinc/terraform-provider-cyral/client" -) - -type DataMapRequest struct { - DataMap `json:"dataMap,omitempty"` -} - -// This is called 'DataMap' and not 'Datamap', because although we consider -// 'datamap' to be a single word in the resource name 'cyral_repository_datamap' -// for ease of writing, 'data map' is actually two words in English. -type DataMap struct { - Labels map[string]*DataMapMapping `json:"labels,omitempty"` -} - -func (dm *DataMap) WriteToSchema(d *schema.ResourceData) error { - var mappings []interface{} - for label, mapping := range dm.Labels { - mappingContents := make(map[string]interface{}) - - var attributes []string - if mapping != nil { - attributes = mapping.Attributes - } - - mappingContents["label"] = label - mappingContents["attributes"] = attributes - - mappings = append(mappings, mappingContents) - } - - return d.Set("mapping", mappings) -} - -func (dm *DataMap) equal(other DataMap) bool { - for label, thisMapping := range dm.Labels { - if otherMapping, ok := other.Labels[label]; ok { - if !elementsMatch(thisMapping.Attributes, otherMapping.Attributes) { - return false - } - } else { - return false - } - } - return true -} - -type DataMapMapping struct { - Attributes []string `json:"attributes,omitempty"` -} - -func resourceRepositoryDatamap() *schema.Resource { - return &schema.Resource{ - Description: "Manages [Data Map](https://cyral.com/docs/policy/datamap).", - CreateContext: resourceRepositoryDatamapCreate, - ReadContext: resourceRepositoryDatamapRead, - UpdateContext: resourceRepositoryDatamapUpdate, - DeleteContext: resourceRepositoryDatamapDelete, - Schema: map[string]*schema.Schema{ - "repository_id": { - Description: "ID of the repository for which to configure a data map.", - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "mapping": { - Description: "Mapping of a label to a list of data locations (attributes).", - Type: schema.TypeSet, - Required: true, - MinItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "label": { - Description: "Label given to the attributes in this mapping.", - Type: schema.TypeString, - Required: true, - }, - "attributes": { - Description: "List containing the specific locations of the data within the repo, " + - "following the pattern `{SCHEMA}.{TABLE}.{ATTRIBUTE}` (ex: " + - "`[your_schema_name.your_table_name.your_attr_name]`).\n\n" + - "-> When referencing data in Dremio repository, please include the complete " + - "location in `attributes`, separating spaces by dots. For example, an attribute " + - "`my_attr` from table `my_tbl` within space `inner_space` within space `outer_space` " + - "would be referenced as `outer_space.inner_space.my_tbl.my_attr`. For more information, " + - "please see the [Policy Guide](https://cyral.com/docs/reference/policy/).", - Type: schema.TypeList, - Required: true, - // TODO: this ForceNew propagates to the parent attribute `mapping`. Therefore, any - // new mapping will force recreation. In the future, it would be good to use the - // `v1/repos/{repoID}/datamap/labels/{label}/attributes/{attribute}` endpoint to - // avoid unnecessary resource recreation. -aholmquist 2022-08-04 - ForceNew: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - }, - }, - }, - }, - Importer: &schema.ResourceImporter{ - StateContext: func( - ctx context.Context, - d *schema.ResourceData, - m interface{}, - ) ([]*schema.ResourceData, error) { - d.Set("repository_id", d.Id()) - return []*schema.ResourceData{d}, nil - }, - }, - } -} - -func resourceRepositoryDatamapCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - log.Printf("[DEBUG] Init resourceRepositoryDatamapCreate") - c := m.(*client.Client) - - repoID := d.Get("repository_id").(string) - url := fmt.Sprintf("https://%s/v1/repos/%s/datamap", c.ControlPlane, repoID) - - dataMap := getDatamapFromResource(d) - dataMapRequest := DataMapRequest{DataMap: dataMap} - - _, err := c.DoRequest(url, http.MethodPut, dataMapRequest) - if err != nil { - return createError("Unable to create repository datamap", err.Error()) - } - - d.SetId(repoID) - // Write data map here to avoid issues with the order of the attributes. - // - // TODO: If in the future the order of the list of attributes the API - // returns becomes deterministic, this can be removed. -aholmquist 2022-08-04 - if err := dataMap.WriteToSchema(d); err != nil { - return createError("Unable to create repository datamap", err.Error()) - } - - log.Printf("[DEBUG] End resourceRepositoryDatamapCreate") - - return resourceRepositoryDatamapRead(ctx, d, m) -} - -func resourceRepositoryDatamapRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - log.Printf("[DEBUG] Init resourceRepositoryDatamapRead") - c := m.(*client.Client) - - repoID := d.Id() - url := fmt.Sprintf("https://%s/v1/repos/%s/datamap", c.ControlPlane, repoID) - - body, err := c.DoRequest(url, http.MethodGet, nil) - if err != nil { - return createError("Unable to create repository datamap", err.Error()) - } - - dataMap := DataMap{} - if err := json.Unmarshal(body, &dataMap); err != nil { - return createError("Unable to unmarshall JSON", err.Error()) - } - log.Printf("[DEBUG] Response body (unmarshalled): %#v", dataMap) - - // TODO: If in the future the order of the list of attributes the API - // returns becomes deterministic, this check can be removed. -aholmquist 2022-08-04 - currentDataMap := getDatamapFromResource(d) - if !currentDataMap.equal(dataMap) { - if err := dataMap.WriteToSchema(d); err != nil { - return createError("Unable to read repository datamap", err.Error()) - } - } - - log.Printf("[DEBUG] End resourceRepositoryDatamapRead") - - return diag.Diagnostics{} -} - -func resourceRepositoryDatamapUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - log.Printf("[DEBUG] Init resourceRepositoryDatamapUpdate") - c := m.(*client.Client) - - repoID := d.Id() - url := fmt.Sprintf("https://%s/v1/repos/%s/datamap", c.ControlPlane, repoID) - - dataMap := getDatamapFromResource(d) - dataMapRequest := DataMapRequest{DataMap: dataMap} - - _, err := c.DoRequest(url, http.MethodPut, dataMapRequest) - if err != nil { - return createError("Unable to create repository datamap", err.Error()) - } - - log.Printf("[DEBUG] End resourceRepositoryDatamapUpdate") - - return resourceRepositoryDatamapRead(ctx, d, m) -} - -func resourceRepositoryDatamapDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - log.Printf("[DEBUG] Init resourceRepositoryDatamapDelete") - c := m.(*client.Client) - - repoID := d.Id() - url := fmt.Sprintf("https://%s/v1/repos/%s/datamap", c.ControlPlane, repoID) - - _, err := c.DoRequest(url, http.MethodDelete, nil) - if err != nil { - return createError("Unable to delete repository datamap", err.Error()) - } - - log.Printf("[DEBUG] End resourceRepositoryDatamapDelete") - - return diag.Diagnostics{} -} - -func getDatamapFromResource(d *schema.ResourceData) DataMap { - mappings := d.Get("mapping").(*schema.Set).List() - - dataMap := DataMap{ - Labels: make(map[string]*DataMapMapping), - } - for _, mappingIface := range mappings { - mapping := mappingIface.(map[string]interface{}) - - label := mapping["label"].(string) - var attributes []string - if mappingAtts, ok := mapping["attributes"]; ok { - for _, attributeIface := range mappingAtts.([]interface{}) { - attributes = append(attributes, attributeIface.(string)) - } - } - dataMap.Labels[label] = &DataMapMapping{ - Attributes: attributes, - } - } - - return dataMap -} diff --git a/tools/tools.go b/cyral/tools/tools.go similarity index 100% rename from tools/tools.go rename to cyral/tools/tools.go diff --git a/cyral/testutils.go b/cyral/utils/testutils.go similarity index 55% rename from cyral/testutils.go rename to cyral/utils/testutils.go index cb6540d4..bd183198 100644 --- a/cyral/testutils.go +++ b/cyral/utils/testutils.go @@ -1,4 +1,4 @@ -package cyral +package utils import ( "fmt" @@ -10,22 +10,28 @@ import ( ) const ( - tprovACCPrefix = "tfprov-acc-" - - basicRepositoryResName = "test_repository" - basicRepositoryID = "cyral_repository.test_repository.id" - basicRepositoryBindingResName = "test_repository_binding" - basicSidecarResName = "test_sidecar" - basicSidecarID = "cyral_sidecar.test_sidecar.id" - basicListenerResName = "test_listener" - basicListenerID = "cyral_sidecar_listener.test_listener.listener_id" - basicPolicyResName = "test_policy" - basicPolicyID = "cyral_policy.test_policy.id" - - testSingleSignOnURL = "https://some-test-sso-url.com" + TFProvACCPrefix = "tfprov-acc-" + + BasicRepositoryResName = "test_repository" + BasicRepositoryID = "cyral_repository.test_repository.id" + BasicRepositoryBindingResName = "test_repository_binding" + BasicSidecarResName = "test_sidecar" + BasicSidecarID = "cyral_sidecar.test_sidecar.id" + BasicListenerResName = "test_listener" + BasicListenerID = "cyral_sidecar_listener.test_listener.listener_id" + BasicPolicyResName = "test_policy" + BasicPolicyID = "cyral_policy.test_policy.id" + + IntegrationIdPResourceName = "integration-idp" + PolicyResourceName = "policy" + RepositoryResourceName = "repository" + RoleResourceName = "role" + SidecarResourceName = "sidecar" + + TestSingleSignOnURL = "https://some-test-sso-url.com" ) -// accTestName attempts to make resource names unique to a specific resource +// AccTestName attempts to make resource names unique to a specific resource // type, and avoid name clashes with other resources that exist in the testing // control plane. // @@ -33,45 +39,27 @@ const ( // // Example usage for cyral_datalabel resource: // -// accTestName("datalabel", "label1") +// AccTestName("datalabel", "label1") // // Example usage for cyral_datalabel data source: // -// accTestName("data-datalabel", "label1") +// AccTestName("data-datalabel", "label1") // // Note that doing it like above will prevent that the tests attempt to create a // label called LABEL1 simultaneously, which would cause a failure. // // The convention is to use hyphen-separated words if possible, and prefix data // sources with "data", to distinguish them and their counterpart resources. -func accTestName(resourceType, suffix string) string { - return fmt.Sprintf("%s%s-%s", tprovACCPrefix, resourceType, suffix) +func AccTestName(resourceType, suffix string) string { + return fmt.Sprintf("%s%s-%s", TFProvACCPrefix, resourceType, suffix) } -func hasAccTestPrefix(name string) bool { - return strings.HasPrefix(name, tprovACCPrefix) +func HasAccTestPrefix(name string) bool { + return strings.HasPrefix(name, TFProvACCPrefix) } -func importStateComposedIDFunc( - resName string, - idAtts []string, - sep string, -) func(*terraform.State) (string, error) { - return func(s *terraform.State) (string, error) { - res, ok := s.RootModule().Resources[resName] - if !ok { - return "", fmt.Errorf("Resource not found: %s", resName) - } - var idParts []string - for _, idAtt := range idAtts { - idParts = append(idParts, res.Primary.Attributes[idAtt]) - } - return marshalComposedID(idParts, sep), nil - } -} - -func formatBasicRepositoryIntoConfig(resName, repoName, typ, host string, port int) string { - if typ == MongoDB { +func FormatBasicRepositoryIntoConfig(resName, repoName, typ, host string, port int) string { + if typ == "mongodb" { return fmt.Sprintf( ` resource "cyral_repository" "%s" { @@ -101,7 +89,7 @@ func formatBasicRepositoryIntoConfig(resName, repoName, typ, host string, port i } } -func formatBasicRepositoryBindingIntoConfig(resName, sidecarID, repositoryID, listenerID string) string { +func FormatBasicRepositoryBindingIntoConfig(resName, sidecarID, repositoryID, listenerID string) string { return fmt.Sprintf( ` resource "cyral_repository_binding" "%s" { @@ -115,7 +103,7 @@ func formatBasicRepositoryBindingIntoConfig(resName, sidecarID, repositoryID, li ) } -func formatBasicSidecarListenerIntoConfig(resName, sidecarID, repoType string, listenerPort int) string { +func FormatBasicSidecarListenerIntoConfig(resName, sidecarID, repoType string, listenerPort int) string { return fmt.Sprintf( ` resource "cyral_sidecar_listener" "%s" { @@ -128,7 +116,7 @@ func formatBasicSidecarListenerIntoConfig(resName, sidecarID, repoType string, l ) } -func formatBasicSidecarIntoConfig(resName, sidecarName, deploymentMethod, logIntegrationID string) string { +func FormatBasicSidecarIntoConfig(resName, sidecarName, deploymentMethod, logIntegrationID string) string { return fmt.Sprintf( ` resource "cyral_sidecar" "%s" { @@ -139,17 +127,17 @@ func formatBasicSidecarIntoConfig(resName, sidecarName, deploymentMethod, logInt ) } -func formatBasicPolicyIntoConfig(name string, data []string) string { +func FormatBasicPolicyIntoConfig(name string, data []string) string { return fmt.Sprintf( ` resource "cyral_policy" "%s" { name = "%s" data = %s - }`, basicPolicyResName, name, listToStr(data), + }`, BasicPolicyResName, name, ListToStr(data), ) } -func formatBasicIntegrationIdPOktaIntoConfig(resName, displayName, ssoURL string) string { +func FormatBasicIntegrationIdPOktaIntoConfig(resName, displayName, ssoURL string) string { return fmt.Sprintf( ` resource "cyral_integration_idp_okta" "%s" { @@ -163,7 +151,7 @@ func formatBasicIntegrationIdPOktaIntoConfig(resName, displayName, ssoURL string ) } -func formatBasicIntegrationIdPSAMLDraftIntoConfig(resName, displayName, idpType string) string { +func FormatBasicIntegrationIdPSAMLDraftIntoConfig(resName, displayName, idpType string) string { return fmt.Sprintf( ` resource "cyral_integration_idp_saml_draft" "%s" { @@ -173,7 +161,7 @@ func formatBasicIntegrationIdPSAMLDraftIntoConfig(resName, displayName, idpType ) } -func formatBasicDataSourcePermissionIntoConfig(resourceName string) string { +func FormatBasicDataSourcePermissionIntoConfig(resourceName string) string { return fmt.Sprintf( ` data "cyral_permission" "%s" {} @@ -181,11 +169,11 @@ func formatBasicDataSourcePermissionIntoConfig(resourceName string) string { ) } -func notZeroRegex() *regexp.Regexp { +func NotZeroRegex() *regexp.Regexp { return regexp.MustCompile("[^0]|([0-9]{2,})") } -// dsourceCheckTypeFilter is used by data source tests that accept type +// DSourceCheckTypeFilter is used by data source tests that accept type // filters. When the data source test is run, there might be unexpected // resources present in the control plane. To avoid test checks that fail // non-deterministically, this function simply checks that all objects match the @@ -193,14 +181,14 @@ func notZeroRegex() *regexp.Regexp { // // Example usage: // -// dsourceCheckTypeFilter( +// DSourceCheckTypeFilter( // // "data.cyral_datalabel.test_datalabel", // "datalabel_list.%d.type", // "CUSTOM", // // ), -func dsourceCheckTypeFilter( +func DSourceCheckTypeFilter( dsourceFullName, typeTemplate, typeFilter string, ) func(s *terraform.State) error { listKey := strings.Split(typeTemplate, ".")[0] @@ -227,3 +215,54 @@ func dsourceCheckTypeFilter( return nil } } + +func DatalabelConfigResourceFullName(resName string) string { + return fmt.Sprintf("cyral_datalabel.%s", resName) +} + +func FormatDataLabelIntoConfig(resName, dataLabelName, dataLabelDescription, + ruleType, ruleCode, ruleStatus string, dataLabelTags []string) string { + var classificationRuleConfig string + if ruleType != "" { + classificationRuleConfig = fmt.Sprintf(` + classification_rule { + rule_type = "%s" + rule_code = "%s" + rule_status = "%s" + }`, + ruleType, + ruleCode, + ruleStatus, + ) + } + return fmt.Sprintf(` + resource "cyral_datalabel" "%s" { + name = "%s" + description = "%s" + tags = %s + %s + }`, + resName, + dataLabelName, + dataLabelDescription, + ListToStr(dataLabelTags), + classificationRuleConfig, + ) +} + +func FormatELKIntegrationDataIntoConfig(name, kibanaURL, esURL string) string { + return fmt.Sprintf(` + resource "cyral_integration_elk" "elk_integration" { + name = "%s" + kibana_url = "%s" + es_url = "%s" + }`, name, kibanaURL, esURL) +} + +func FormatDatadogIntegrationDataIntoConfig(name, apiKey string) string { + return fmt.Sprintf(` + resource "cyral_integration_datadog" "datadog_integration" { + name = "%s" + api_key = "%s" + }`, name, apiKey) +} diff --git a/cyral/utils.go b/cyral/utils/utils.go similarity index 70% rename from cyral/utils.go rename to cyral/utils/utils.go index 5789ec00..01243be3 100644 --- a/cyral/utils.go +++ b/cyral/utils/utils.go @@ -1,4 +1,4 @@ -package cyral +package utils import ( "fmt" @@ -7,6 +7,7 @@ import ( "sort" "strings" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) @@ -23,9 +24,10 @@ const ( BindingIDKey = "binding_id" SidecarIDKey = "sidecar_id" ListenerIDKey = "listener_id" + StatusKey = "status" ) -func convertToInterfaceList[T any](list []T) []any { +func ConvertToInterfaceList[T any](list []T) []any { if list == nil { return nil } @@ -36,7 +38,7 @@ func convertToInterfaceList[T any](list []T) []any { return interfaceList } -func convertFromInterfaceList[T any](interfaceList []any) []T { +func ConvertFromInterfaceList[T any](interfaceList []any) []T { if interfaceList == nil { return nil } @@ -47,7 +49,7 @@ func convertFromInterfaceList[T any](interfaceList []any) []T { return list } -func urlQuery(kv map[string]string) string { +func UrlQuery(kv map[string]string) string { queryStr := "?" for k, v := range kv { queryStr += fmt.Sprintf("&%s=%s", k, v) @@ -55,7 +57,7 @@ func urlQuery(kv map[string]string) string { return queryStr } -func elementsMatch(this, other []string) bool { +func ElementsMatch(this, other []string) bool { if len(this) != len(other) { return false } @@ -71,7 +73,7 @@ func elementsMatch(this, other []string) bool { return true } -func listToStr(attributes []string) string { +func ListToStr(attributes []string) string { if len(attributes) == 0 { return "[]" } @@ -86,7 +88,7 @@ func listToStr(attributes []string) string { return s } -func listToStrNoQuotes(l []string) string { +func ListToStrNoQuotes(l []string) string { if len(l) == 0 { return "[]" } @@ -101,7 +103,7 @@ func listToStrNoQuotes(l []string) string { return s } -func supportedTypesMarkdown(types []string) string { +func SupportedValuesAsMarkdown(types []string) string { var s string for _, typ := range types { s += fmt.Sprintf("\n - `%s`", typ) @@ -109,11 +111,11 @@ func supportedTypesMarkdown(types []string) string { return s } -func marshalComposedID(ids []string, sep string) string { +func MarshalComposedID(ids []string, sep string) string { return strings.Join(ids, sep) } -func unmarshalComposedID(id, sep string, numFields int) ([]string, error) { +func UnMarshalComposedID(id, sep string, numFields int) ([]string, error) { ids := strings.Split(id, sep) if len(ids) < numFields { return nil, fmt.Errorf("unexpected ID syntax. Correct ID " + @@ -123,11 +125,15 @@ func unmarshalComposedID(id, sep string, numFields int) ([]string, error) { return ids, nil } -func validationStringLenAtLeast(min int) schema.SchemaValidateFunc { +func ValidationStringLenAtLeast(min int) schema.SchemaValidateFunc { return validation.StringLenBetween(min, math.MaxInt) } -func validationDurationString(value interface{}, key string) (warnings []string, errors []error) { +func TypeSetNonEmpty(d *schema.ResourceData, attname string) bool { + return len(d.Get(attname).(*schema.Set).List()) > 0 +} + +func ValidationDurationString(value interface{}, key string) (warnings []string, errors []error) { duration, ok := value.(string) if !ok { errors = append(errors, fmt.Errorf("expected type of %s to be string", key)) @@ -142,11 +148,7 @@ func validationDurationString(value interface{}, key string) (warnings []string, return warnings, errors } -func typeSetNonEmpty(d *schema.ResourceData, attname string) bool { - return len(d.Get(attname).(*schema.Set).List()) > 0 -} - -func getStrList(m map[string]interface{}, attName string) []string { +func GetStrList(m map[string]interface{}, attName string) []string { var attStrs []string for _, valIface := range m[attName].([]interface{}) { attStrs = append(attStrs, valIface.(string)) @@ -154,7 +156,7 @@ func getStrList(m map[string]interface{}, attName string) []string { return attStrs } -func convertSchemaFieldsToComputed(s map[string]*schema.Schema) map[string]*schema.Schema { +func ConvertSchemaFieldsToComputed(s map[string]*schema.Schema) map[string]*schema.Schema { for k, _ := range s { s[k] = &schema.Schema{ Description: s[k].Description, @@ -163,21 +165,41 @@ func convertSchemaFieldsToComputed(s map[string]*schema.Schema) map[string]*sche Elem: s[k].Elem, } if elem, ok := s[k].Elem.(*schema.Resource); ok && elem != nil { - convertSchemaFieldsToComputed(elem.Schema) + ConvertSchemaFieldsToComputed(elem.Schema) } } return s } -// setKeysAsNewComputedIfPlanHasChanges is intended to be used in resource CustomizeDiff functions to set +func CreateError(summary, detail string) diag.Diagnostics { + var diags diag.Diagnostics + + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: summary, + Detail: detail, + }) + + return diags +} + +func ToSliceOfString[T any](s []T, f func(T) string) []string { + result := make([]string, len(s)) + for i, v := range s { + result[i] = f(v) + } + return result +} + +// SetKeysAsNewComputedIfPlanHasChanges is intended to be used in resource CustomizeDiff functions to set // computed fields that are expected to change as "new computed" (known after apply) so that terraform can // detect changes in those fields and update them in the resource state correctly in the same plan operation. // Otherwise, if this function is not called, terraform will not detect a change in those computed fields during // the initial update operation and the changes will only be detected in the subsequent terraform plan. // For reference: // - https://github.com/hashicorp/terraform/issues/15857 -func setKeysAsNewComputedIfPlanHasChanges(resourceDiff *schema.ResourceDiff, keys []string) { +func SetKeysAsNewComputedIfPlanHasChanges(resourceDiff *schema.ResourceDiff, keys []string) { changedKeys := resourceDiff.GetChangedKeysPrefix("") log.Printf("[DEBUG] changedKeys: %+v", changedKeys) hasChanges := len(changedKeys) > 0 @@ -188,3 +210,13 @@ func setKeysAsNewComputedIfPlanHasChanges(resourceDiff *schema.ResourceDiff, key } } } + +func GetStrListFromSchemaField(d *schema.ResourceData, field string) []string { + strList := []string{} + + for _, v := range d.Get(field).([]interface{}) { + strList = append(strList, v.(string)) + } + + return strList +} diff --git a/cyral/utils_test.go b/cyral/utils/utils_test.go similarity index 93% rename from cyral/utils_test.go rename to cyral/utils/utils_test.go index 4923279c..829303d3 100644 --- a/cyral/utils_test.go +++ b/cyral/utils/utils_test.go @@ -1,4 +1,4 @@ -package cyral +package utils import ( "testing" @@ -37,7 +37,7 @@ func TestElementsMatch(t *testing.T) { } for _, testCase := range testCases { - match := elementsMatch(testCase.this, testCase.other) + match := ElementsMatch(testCase.this, testCase.other) if match != testCase.expectMatch { t.Errorf("For test %q, expected match=%t got match=%t", testCase.desc, testCase.expectMatch, match) diff --git a/cyral/utils_api.go b/cyral/utils_api.go deleted file mode 100644 index 7c53b770..00000000 --- a/cyral/utils_api.go +++ /dev/null @@ -1,101 +0,0 @@ -package cyral - -import ( - "encoding/json" - "fmt" - "log" - "net/http" - - "github.com/cyralinc/terraform-provider-cyral/client" -) - -func listSidecars(c *client.Client) ([]IdentifiedSidecarInfo, error) { - log.Printf("[DEBUG] Init listSidecars") - url := fmt.Sprintf("https://%s/v1/sidecars", c.ControlPlane) - body, err := c.DoRequest(url, http.MethodGet, nil) - if err != nil { - return nil, err - } - - var sidecarsInfo []IdentifiedSidecarInfo - if err := json.Unmarshal(body, &sidecarsInfo); err != nil { - return nil, err - } - log.Printf("[DEBUG] Response body (unmarshalled): %#v", sidecarsInfo) - log.Printf("[DEBUG] End listSidecars") - - return sidecarsInfo, nil -} - -func listRoles(c *client.Client) (*GetUserGroupsResponse, error) { - log.Printf("[DEBUG] Init listRoles") - - url := fmt.Sprintf("https://%s/v1/users/groups", c.ControlPlane) - body, err := c.DoRequest(url, http.MethodGet, nil) - if err != nil { - return nil, err - } - resp := &GetUserGroupsResponse{} - if err := json.Unmarshal(body, resp); err != nil { - return nil, err - } - log.Printf("[DEBUG] Response body (unmarshalled): %#v", resp) - log.Printf("[DEBUG] End listRoles") - - return resp, nil -} - -func listIdPIntegrations(c *client.Client) (*IdPIntegrations, error) { - log.Printf("[DEBUG] Init listIdPIntegrations") - - url := fmt.Sprintf("https://%s/v1/integrations/saml", c.ControlPlane) - body, err := c.DoRequest(url, http.MethodGet, nil) - if err != nil { - return nil, err - } - resp := &IdPIntegrations{} - if err := json.Unmarshal(body, resp); err != nil { - return nil, err - } - log.Printf("[DEBUG] Response body (unmarshalled): %#v", resp) - log.Printf("[DEBUG] End listIdPIntegrations") - - return resp, nil -} - -func listPolicies(c *client.Client) ([]Policy, error) { - log.Printf("[DEBUG] Init listPolicies") - - url := fmt.Sprintf("https://%s/v1/policies", c.ControlPlane) - resp, err := c.DoRequest(url, http.MethodGet, nil) - if err != nil { - return nil, err - } - - var listResp PolicyListResponse - if err := json.Unmarshal(resp, &listResp); err != nil { - return nil, err - } - log.Printf("[DEBUG] Response body (unmarshalled): %#v", listResp) - - var policies []Policy - for _, policyID := range listResp.Policies { - url := fmt.Sprintf("https://%s/v1/policies/%s", - c.ControlPlane, policyID) - resp, err := c.DoRequest(url, http.MethodGet, nil) - if err != nil { - return nil, err - } - - var policy Policy - if err := json.Unmarshal(resp, &policy); err != nil { - return nil, err - } - log.Printf("[DEBUG] Response body (unmarshalled): %#v", policy) - - policies = append(policies, policy) - } - - log.Printf("[DEBUG] End listPolicies") - return policies, nil -} diff --git a/docs/resources/datalabel.md b/docs/resources/datalabel.md index da95eff7..f4e03b49 100644 --- a/docs/resources/datalabel.md +++ b/docs/resources/datalabel.md @@ -50,5 +50,9 @@ resource "cyral_datalabel" "NAME" { Optional: - `rule_code` (String) Actual code of the classification rule. For example, this attribute may contain REGO code for `REGO`-type classification rules. -- `rule_status` (String) Status of the classification rule. Valid values are: `ENABLED` and `DISABLED`. Defaults to `ENABLED`. -- `rule_type` (String) Type of the classification rule. Valid values are: `UNKNOWN` and `REGO`. Defaults to `UNKNOWN`. +- `rule_status` (String) Status of the classification rule. List of supported values: + - `ENABLED` + - `DISABLED` +- `rule_type` (String) Type of the classification rule. List of supported values: + - `UNKNOWN` + - `REGO` diff --git a/docs/resources/repository_datamap.md b/docs/resources/repository_datamap.md index 547143b4..ef3209e0 100644 --- a/docs/resources/repository_datamap.md +++ b/docs/resources/repository_datamap.md @@ -17,8 +17,11 @@ Manages [Data Map](https://cyral.com/docs/policy/datamap). resource "cyral_repository" "example-pg" { name = "example-pg" type = "postgresql" - host = "pg.example.com" - port = 5432 + + repo_node { + host = "pg.example.com" + port = 5432 + } } # Create custom labels diff --git a/docs/resources/repository_user_account.md b/docs/resources/repository_user_account.md index 42b0ca8c..f7c01926 100644 --- a/docs/resources/repository_user_account.md +++ b/docs/resources/repository_user_account.md @@ -11,93 +11,88 @@ account in the Cyral Control Plane. ```terraform # Test Repo to use in examples. resource "cyral_repository" "tf_test_repo" { + name = "tf_test_repo" + type = "postgresql" + + repo_node { host = "postgresql.mycompany.com" - name = "tf_test_repo" port = 5432 - type = "postgresql" + } } # cyral_repository_user_account with auth scheme aws_iam resource "cyral_repository_user_account" "aws_iam" { - name = "hbf_aws_iam" - repository_id = cyral_repository.tf_test_repo.id + name = "hbf_aws_iam" + repository_id = cyral_repository.tf_test_repo.id - auth_scheme { - aws_iam { - role_arn = "role_arn" - } + auth_scheme { + aws_iam { + role_arn = "role_arn" } + } } # cyral_repository_user_account with auth scheme aws_secrets will be created resource "cyral_repository_user_account" "aws_secrets" { + name = "hbf_aws_secrets" + repository_id = cyral_repository.tf_test_repo.id - name = "hbf_aws_secrets" - repository_id = cyral_repository.tf_test_repo.id - - auth_scheme { - aws_secrets_manager { - secret_arn = "secret_arn" - } + auth_scheme { + aws_secrets_manager { + secret_arn = "secret_arn" } + } } # cyral_repository_user_account with auth scheme env_var will be created resource "cyral_repository_user_account" "env_var" { + name = "hbf_env_var" + repository_id = cyral_repository.tf_test_repo.id - name = "hbf_env_var" - repository_id = cyral_repository.tf_test_repo.id - - auth_scheme { - - environment_variable { - variable_name = "some-env-var" - } + auth_scheme { + environment_variable { + variable_name = "some-env-var" } + } } # cyral_repository_user_account with auth scheme gcp_secrets will be created resource "cyral_repository_user_account" "gcp_secrets" { + name = "hbf_gcp_secrets" + repository_id = cyral_repository.tf_test_repo.id - name = "hbf_gcp_secrets" - repository_id = cyral_repository.tf_test_repo.id - - auth_scheme { - - gcp_secrets_manager { - secret_name = "secret_name" - } + auth_scheme { + gcp_secrets_manager { + secret_name = "secret_name" } + } } # cyral_repository_user_account with auth scheme hashicorp will be created resource "cyral_repository_user_account" "hashicorp" { + name = "hbf_hashicorp" + repository_id = cyral_repository.tf_test_repo.id - name = "hbf_hashicorp" - repository_id = cyral_repository.tf_test_repo.id - - auth_scheme { - hashicorp_vault { - path = "some-path" - is_dynamic_user_account = false - } + auth_scheme { + hashicorp_vault { + path = "some-path" + is_dynamic_user_account = false } + } } # cyral_repository_user_account with auth scheme kubernetes will be created resource "cyral_repository_user_account" "kubernetes" { + name = "hbf_kubernetes" + repository_id = cyral_repository.tf_test_repo.id - name = "hbf_kubernetes" - repository_id = cyral_repository.tf_test_repo.id - - auth_scheme { - - kubernetes_secret { - secret_key = "secret_key" - secret_name = "secret_name" - } + auth_scheme { + kubernetes_secret { + secret_key = "secret_key" + secret_name = "secret_name" } + } } ``` diff --git a/examples/resources/cyral_repository_datamap/resource.tf b/examples/resources/cyral_repository_datamap/resource.tf index 47e0a1cc..1ef353e5 100644 --- a/examples/resources/cyral_repository_datamap/resource.tf +++ b/examples/resources/cyral_repository_datamap/resource.tf @@ -2,8 +2,11 @@ resource "cyral_repository" "example-pg" { name = "example-pg" type = "postgresql" - host = "pg.example.com" - port = 5432 + + repo_node { + host = "pg.example.com" + port = 5432 + } } # Create custom labels diff --git a/examples/resources/cyral_repository_user_account/resource.tf b/examples/resources/cyral_repository_user_account/resource.tf index ffd7c10e..4dea5d54 100644 --- a/examples/resources/cyral_repository_user_account/resource.tf +++ b/examples/resources/cyral_repository_user_account/resource.tf @@ -1,90 +1,85 @@ # Test Repo to use in examples. resource "cyral_repository" "tf_test_repo" { + name = "tf_test_repo" + type = "postgresql" + + repo_node { host = "postgresql.mycompany.com" - name = "tf_test_repo" port = 5432 - type = "postgresql" + } } # cyral_repository_user_account with auth scheme aws_iam resource "cyral_repository_user_account" "aws_iam" { - name = "hbf_aws_iam" - repository_id = cyral_repository.tf_test_repo.id + name = "hbf_aws_iam" + repository_id = cyral_repository.tf_test_repo.id - auth_scheme { - aws_iam { - role_arn = "role_arn" - } + auth_scheme { + aws_iam { + role_arn = "role_arn" } + } } # cyral_repository_user_account with auth scheme aws_secrets will be created resource "cyral_repository_user_account" "aws_secrets" { + name = "hbf_aws_secrets" + repository_id = cyral_repository.tf_test_repo.id - name = "hbf_aws_secrets" - repository_id = cyral_repository.tf_test_repo.id - - auth_scheme { - aws_secrets_manager { - secret_arn = "secret_arn" - } + auth_scheme { + aws_secrets_manager { + secret_arn = "secret_arn" } + } } # cyral_repository_user_account with auth scheme env_var will be created resource "cyral_repository_user_account" "env_var" { + name = "hbf_env_var" + repository_id = cyral_repository.tf_test_repo.id - name = "hbf_env_var" - repository_id = cyral_repository.tf_test_repo.id - - auth_scheme { - - environment_variable { - variable_name = "some-env-var" - } + auth_scheme { + environment_variable { + variable_name = "some-env-var" } + } } # cyral_repository_user_account with auth scheme gcp_secrets will be created resource "cyral_repository_user_account" "gcp_secrets" { + name = "hbf_gcp_secrets" + repository_id = cyral_repository.tf_test_repo.id - name = "hbf_gcp_secrets" - repository_id = cyral_repository.tf_test_repo.id - - auth_scheme { - - gcp_secrets_manager { - secret_name = "secret_name" - } + auth_scheme { + gcp_secrets_manager { + secret_name = "secret_name" } + } } # cyral_repository_user_account with auth scheme hashicorp will be created resource "cyral_repository_user_account" "hashicorp" { + name = "hbf_hashicorp" + repository_id = cyral_repository.tf_test_repo.id - name = "hbf_hashicorp" - repository_id = cyral_repository.tf_test_repo.id - - auth_scheme { - hashicorp_vault { - path = "some-path" - is_dynamic_user_account = false - } + auth_scheme { + hashicorp_vault { + path = "some-path" + is_dynamic_user_account = false } + } } # cyral_repository_user_account with auth scheme kubernetes will be created resource "cyral_repository_user_account" "kubernetes" { + name = "hbf_kubernetes" + repository_id = cyral_repository.tf_test_repo.id - name = "hbf_kubernetes" - repository_id = cyral_repository.tf_test_repo.id - - auth_scheme { - - kubernetes_secret { - secret_key = "secret_key" - secret_name = "secret_name" - } + auth_scheme { + kubernetes_secret { + secret_key = "secret_key" + secret_name = "secret_name" } + } } diff --git a/go.mod b/go.mod index 7c4b0b36..f3987252 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/cyralinc/terraform-provider-cyral -go 1.19 +go 1.20 require ( github.com/aws/aws-sdk-go v1.44.327 diff --git a/main.go b/main.go index 1c6ab922..206a4248 100644 --- a/main.go +++ b/main.go @@ -1,12 +1,12 @@ package main import ( - "github.com/cyralinc/terraform-provider-cyral/cyral" + "github.com/cyralinc/terraform-provider-cyral/cyral/provider" "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" ) func main() { plugin.Serve(&plugin.ServeOpts{ - ProviderFunc: cyral.Provider, + ProviderFunc: provider.Provider, }) }