From 9fa29e33d5a77d26cb0b5602eb81d83b6c00982b Mon Sep 17 00:00:00 2001 From: Heng Lu Date: Fri, 7 May 2021 09:37:59 +0800 Subject: [PATCH] add resource azurerm_cosmosdb_sql_user_defined_function --- .../internal/services/cosmos/client/client.go | 5 + .../cosmos/cosmosdb_sql_function_resource.go | 156 ++++++++++++++ .../cosmosdb_sql_function_resource_test.go | 190 ++++++++++++++++++ .../services/cosmos/parse/sql_function.go | 87 ++++++++ .../cosmos/parse/sql_function_test.go | 160 +++++++++++++++ .../internal/services/cosmos/registration.go | 1 + .../internal/services/cosmos/resourceids.go | 1 + .../cosmos/validate/sql_function_id.go | 23 +++ .../cosmos/validate/sql_function_id_test.go | 112 +++++++++++ ...db_sql_user_defined_function.html.markdown | 74 +++++++ 10 files changed, 809 insertions(+) create mode 100644 azurerm/internal/services/cosmos/cosmosdb_sql_function_resource.go create mode 100644 azurerm/internal/services/cosmos/cosmosdb_sql_function_resource_test.go create mode 100644 azurerm/internal/services/cosmos/parse/sql_function.go create mode 100644 azurerm/internal/services/cosmos/parse/sql_function_test.go create mode 100644 azurerm/internal/services/cosmos/validate/sql_function_id.go create mode 100644 azurerm/internal/services/cosmos/validate/sql_function_id_test.go create mode 100644 website/docs/r/cosmosdb_sql_user_defined_function.html.markdown diff --git a/azurerm/internal/services/cosmos/client/client.go b/azurerm/internal/services/cosmos/client/client.go index 72dec52fcf60..d56d2278eed3 100644 --- a/azurerm/internal/services/cosmos/client/client.go +++ b/azurerm/internal/services/cosmos/client/client.go @@ -12,6 +12,7 @@ type Client struct { MongoDbClient *documentdb.MongoDBResourcesClient NotebookWorkspaceClient *documentdb.NotebookWorkspacesClient SqlClient *documentdb.SQLResourcesClient + SqlResourceClient *documentdb.SQLResourcesClient TableClient *documentdb.TableResourcesClient } @@ -34,6 +35,9 @@ func NewClient(o *common.ClientOptions) *Client { sqlClient := documentdb.NewSQLResourcesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&sqlClient.Client, o.ResourceManagerAuthorizer) + sqlResourceClient := documentdb.NewSQLResourcesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&sqlResourceClient.Client, o.ResourceManagerAuthorizer) + tableClient := documentdb.NewTableResourcesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&tableClient.Client, o.ResourceManagerAuthorizer) @@ -44,6 +48,7 @@ func NewClient(o *common.ClientOptions) *Client { MongoDbClient: &mongoDbClient, NotebookWorkspaceClient: ¬ebookWorkspaceClient, SqlClient: &sqlClient, + SqlResourceClient: &sqlResourceClient, TableClient: &tableClient, } } diff --git a/azurerm/internal/services/cosmos/cosmosdb_sql_function_resource.go b/azurerm/internal/services/cosmos/cosmosdb_sql_function_resource.go new file mode 100644 index 000000000000..a1b6d30fed3a --- /dev/null +++ b/azurerm/internal/services/cosmos/cosmosdb_sql_function_resource.go @@ -0,0 +1,156 @@ +package cosmos + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/cosmos-db/mgmt/2021-01-15/documentdb" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/cosmos/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/cosmos/validate" + azSchema "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceCosmosDbSQLFunction() *schema.Resource { + return &schema.Resource{ + Create: resourceCosmosDbSQLFunctionCreateUpdate, + Read: resourceCosmosDbSQLFunctionRead, + Update: resourceCosmosDbSQLFunctionCreateUpdate, + Delete: resourceCosmosDbSQLFunctionDelete, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Read: schema.DefaultTimeout(5 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + + Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error { + _, err := parse.SqlFunctionID(id) + return err + }), + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "container_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.SqlContainerID, + }, + + "body": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + } +} +func resourceCosmosDbSQLFunctionCreateUpdate(d *schema.ResourceData, meta interface{}) error { + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + client := meta.(*clients.Client).Cosmos.SqlResourceClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + name := d.Get("name").(string) + containerId, _ := parse.SqlContainerID(d.Get("container_id").(string)) + body := d.Get("body").(string) + + id := parse.NewSqlFunctionID(subscriptionId, containerId.ResourceGroup, containerId.DatabaseAccountName, containerId.SqlDatabaseName, containerId.ContainerName, name) + + if d.IsNewResource() { + existing, err := client.GetSQLUserDefinedFunction(ctx, id.ResourceGroup, id.DatabaseAccountName, id.SqlDatabaseName, id.ContainerName, id.UserDefinedFunctionName) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for existing CosmosDb SqlFunction %q: %+v", id, err) + } + } + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_cosmosdb_sql_function", id.ID()) + } + } + + createUpdateSqlUserDefinedFunctionParameters := documentdb.SQLUserDefinedFunctionCreateUpdateParameters{ + SQLUserDefinedFunctionCreateUpdateProperties: &documentdb.SQLUserDefinedFunctionCreateUpdateProperties{ + Resource: &documentdb.SQLUserDefinedFunctionResource{ + ID: &name, + Body: &body, + }, + Options: &documentdb.CreateUpdateOptions{}, + }, + } + future, err := client.CreateUpdateSQLUserDefinedFunction(ctx, id.ResourceGroup, id.DatabaseAccountName, id.SqlDatabaseName, id.ContainerName, id.UserDefinedFunctionName, createUpdateSqlUserDefinedFunctionParameters) + if err != nil { + return fmt.Errorf("creating/updating CosmosDb SqlFunction %q: %+v", id, err) + } + + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for creation/update of the CosmosDb SqlFunction %q: %+v", id, err) + } + + d.SetId(id.ID()) + return resourceCosmosDbSQLFunctionRead(d, meta) +} + +func resourceCosmosDbSQLFunctionRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cosmos.SqlResourceClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.SqlFunctionID(d.Id()) + if err != nil { + return err + } + + resp, err := client.GetSQLUserDefinedFunction(ctx, id.ResourceGroup, id.DatabaseAccountName, id.SqlDatabaseName, id.ContainerName, id.UserDefinedFunctionName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] CosmosDb SqlFunction %q does not exist - removing from state", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("retrieving CosmosDb SqlFunction %q: %+v", id, err) + } + containerId := parse.NewSqlContainerID(id.SubscriptionId, id.ResourceGroup, id.DatabaseAccountName, id.SqlDatabaseName, id.ContainerName) + d.Set("name", id.UserDefinedFunctionName) + d.Set("container_id", containerId.ID()) + if props := resp.SQLUserDefinedFunctionGetProperties; props != nil { + if props.Resource != nil { + d.Set("body", props.Resource.Body) + } + } + return nil +} + +func resourceCosmosDbSQLFunctionDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cosmos.SqlResourceClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.SqlFunctionID(d.Id()) + if err != nil { + return err + } + + future, err := client.DeleteSQLUserDefinedFunction(ctx, id.ResourceGroup, id.DatabaseAccountName, id.SqlDatabaseName, id.ContainerName, id.UserDefinedFunctionName) + if err != nil { + return fmt.Errorf("deleting CosmosDb SqlFunction %q: %+v", id, err) + } + + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for deletion of the CosmosDb SqlFunction %q: %+v", id, err) + } + return nil +} diff --git a/azurerm/internal/services/cosmos/cosmosdb_sql_function_resource_test.go b/azurerm/internal/services/cosmos/cosmosdb_sql_function_resource_test.go new file mode 100644 index 000000000000..7bb9c6334ae0 --- /dev/null +++ b/azurerm/internal/services/cosmos/cosmosdb_sql_function_resource_test.go @@ -0,0 +1,190 @@ +package cosmos_test + +import ( + "context" + "fmt" + "testing" + + "github.com/Azure/azure-sdk-for-go/services/cosmos-db/mgmt/2021-01-15/documentdb" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance/check" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/cosmos/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +type CosmosDbSQLFunctionResource struct{} + +func TestAccCosmosDbSQLFunction_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cosmosdb_sql_function", "test") + r := CosmosDbSQLFunctionResource{} + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccCosmosDbSQLFunction_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cosmosdb_sql_function", "test") + r := CosmosDbSQLFunctionResource{} + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccCosmosDbSQLFunction_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cosmosdb_sql_function", "test") + r := CosmosDbSQLFunctionResource{} + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.update(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r CosmosDbSQLFunctionResource) Exists(ctx context.Context, client *clients.Client, state *terraform.InstanceState) (*bool, error) { + id, err := parse.SqlFunctionID(state.ID) + if err != nil { + return nil, err + } + resp, err := client.Cosmos.SqlResourceClient.GetSQLUserDefinedFunction(ctx, id.ResourceGroup, id.DatabaseAccountName, id.SqlDatabaseName, id.ContainerName, id.UserDefinedFunctionName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving CosmosDb SQLFunction %q: %+v", id, err) + } + return utils.Bool(true), nil +} + +func (r CosmosDbSQLFunctionResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-cosmos-%[2]d" + location = "%[1]s" +} + +resource "azurerm_cosmosdb_account" "test" { + name = "acctest-cosmos-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + offer_type = "Standard" + kind = "%[3]s" + + consistency_policy { + consistency_level = "%[4]s" + } + + geo_location { + location = azurerm_resource_group.test.location + failover_priority = 0 + } +} + +resource "azurerm_cosmosdb_sql_database" "test" { + name = "acctest-sql-database-%[2]d" + resource_group_name = azurerm_cosmosdb_account.test.resource_group_name + account_name = azurerm_cosmosdb_account.test.name +} + +resource "azurerm_cosmosdb_sql_container" "test" { + name = "acctest-sql-container-%[2]d" + resource_group_name = azurerm_cosmosdb_account.test.resource_group_name + account_name = azurerm_cosmosdb_account.test.name + database_name = azurerm_cosmosdb_sql_database.test.name + partition_key_path = "/definition/id" +} + `, data.Locations.Primary, data.RandomInteger, string(documentdb.GlobalDocumentDB), string(documentdb.Session)) +} + +func (r CosmosDbSQLFunctionResource) basic(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_cosmosdb_sql_function" "test" { + name = "acctest-dssdf-%d" + container_id = azurerm_cosmosdb_sql_container.test.id + body = <