Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add cosmos db/keyspace/table resources #3442

Merged
merged 25 commits into from
May 16, 2019
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions azurerm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ type ArmClient struct {

StopContext context.Context

cosmosDBClient documentdb.DatabaseAccountsClient
cosmosAccountsClient documentdb.DatabaseAccountsClient

automationAccountClient automation.AccountClient
automationAgentRegistrationInfoClient automation.AgentRegistrationInformationClient
Expand Down Expand Up @@ -498,7 +498,7 @@ func getArmClient(c *authentication.Config, skipProviderRegistration bool, partn
client.registerContainerInstanceClients(endpoint, c.SubscriptionID, auth)
client.registerContainerRegistryClients(endpoint, c.SubscriptionID, auth)
client.registerContainerServicesClients(endpoint, c.SubscriptionID, auth)
client.registerCosmosDBClients(endpoint, c.SubscriptionID, auth)
client.registerCosmosAccountsClients(endpoint, c.SubscriptionID, auth)
client.registerDatabricksClients(endpoint, c.SubscriptionID, auth)
client.registerDatabases(endpoint, c.SubscriptionID, auth, sender)
client.registerDataFactoryClients(endpoint, c.SubscriptionID, auth)
Expand Down Expand Up @@ -735,10 +735,10 @@ func (c *ArmClient) registerCognitiveServiceClients(endpoint, subscriptionId str
c.cognitiveAccountsClient = accountsClient
}

func (c *ArmClient) registerCosmosDBClients(endpoint, subscriptionId string, auth autorest.Authorizer) {
cdb := documentdb.NewDatabaseAccountsClientWithBaseURI(endpoint, subscriptionId)
c.configureClient(&cdb.Client, auth)
c.cosmosDBClient = cdb
func (c *ArmClient) registerCosmosAccountsClients(endpoint, subscriptionId string, auth autorest.Authorizer) {
ca := documentdb.NewDatabaseAccountsClientWithBaseURI(endpoint, subscriptionId)
c.configureClient(&ca.Client, auth)
c.cosmosAccountsClient = ca
}

func (c *ArmClient) registerMediaServiceClients(endpoint, subscriptionId string, auth autorest.Authorizer) {
Expand Down
2 changes: 1 addition & 1 deletion azurerm/data_source_cosmos_db_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func dataSourceArmCosmosDBAccount() *schema.Resource {
}

func dataSourceArmCosmosDBAccountRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).cosmosDBClient
client := meta.(*ArmClient).cosmosAccountsClient
ctx := meta.(*ArmClient).StopContext

resourceGroup := d.Get("resource_group_name").(string)
Expand Down
128 changes: 128 additions & 0 deletions azurerm/helpers/azure/cosmos.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package azure

import (
"fmt"

"github.com/Azure/go-autorest/autorest"
)

// it seems the cosmos API is not returning any sort of valid ID in the main response body
// so lets grab it from the response.request.url.path
func CosmosGetIDFromResponse(resp autorest.Response) (string, error) {
if resp.Response == nil {
return "", fmt.Errorf("Error: Unable to get Cosmos ID from Response: http response is nil")
}

if resp.Response.Request == nil {
return "", fmt.Errorf("Error: Unable to get Cosmos ID from Response: Request is nil")
}

if resp.Response.Request.URL == nil {
return "", fmt.Errorf("Error: Unable to get Cosmos ID from Response: URL is nil")
}

return resp.Response.Request.URL.Path, nil
}

type CosmosAccountID struct {
ResourceID
Account string
}

func ParseCosmosAccountID(id string) (*CosmosAccountID, error) {
subid, err := ParseAzureResourceID(id)
if err != nil {
return nil, err
}

account, ok := subid.Path["databaseAccounts"]
if !ok {
return nil, fmt.Errorf("Error: Unable to parse Cosmos Database Resource ID: databaseAccounts is missing from: %s", id)
}

return &CosmosAccountID{
ResourceID: *subid,
Account: account,
}, nil
}

type CosmosDatabaseID struct {
CosmosAccountID
Database string
}

func ParseCosmosDatabaseID(id string) (*CosmosDatabaseID, error) {
subid, err := ParseCosmosAccountID(id)
if err != nil {
return nil, err
}

db, ok := subid.Path["databases"]
if !ok {
return nil, fmt.Errorf("Error: Unable to parse Cosmos Database Resource ID: databases is missing from: %s", id)
}

return &CosmosDatabaseID{
CosmosAccountID: *subid,
Database: db,
}, nil
}

type CosmosDatabaseCollectionID struct {
CosmosDatabaseID
Collection string
}

type CosmosDatabaseContainerID struct {
CosmosDatabaseID
Container string
}

type CosmosKeyspaceID struct {
CosmosAccountID
Keyspace string
}

func ParseCosmosKeyspaceID(id string) (*CosmosKeyspaceID, error) {
subid, err := ParseCosmosAccountID(id)
if err != nil {
return nil, err
}

ks, ok := subid.Path["keyspaces"]
if !ok {
return nil, fmt.Errorf("Error: Unable to parse Cosmos Keyspace Resource ID: keyspaces is missing from: %s", id)
}

return &CosmosKeyspaceID{
CosmosAccountID: *subid,
Keyspace: ks,
}, nil
}

type CosmosKeyspaceTableID struct {
CosmosKeyspaceID
Table string
}

type CosmosTableID struct {
CosmosAccountID
Table string
}

func ParseCosmosTableID(id string) (*CosmosTableID, error) {
subid, err := ParseCosmosAccountID(id)
if err != nil {
return nil, err
}

table, ok := subid.Path["tables"]
if !ok {
return nil, fmt.Errorf("Error: Unable to parse Cosmos Table Resource ID: tables is missing from: %s", id)
}

return &CosmosTableID{
CosmosAccountID: *subid,
Table: table,
}, nil
}
12 changes: 12 additions & 0 deletions azurerm/helpers/tf/acctest.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tf

import (
"fmt"
"strconv"
"strings"
"time"
Expand All @@ -25,3 +26,14 @@ func AccRandTimeInt() int {

return i
}

func AccCheckResourceAttributes(attributes map[string]string, check ...string) error {
for _, a := range check {
_, ok := attributes[a]
if !ok {
return fmt.Errorf("missing %s", a)
}
}

return nil
}
28 changes: 28 additions & 0 deletions azurerm/helpers/validate/cosmos.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package validate

import (
"fmt"
"regexp"
)

func CosmosAccountName(v interface{}, k string) (warnings []string, errors []error) {
value := v.(string)

// Portal: The value must contain only alphanumeric characters or the following: -
if matched := regexp.MustCompile("^[-a-z0-9]{3,50}$").Match([]byte(value)); !matched {
errors = append(errors, fmt.Errorf("%s name must be 3 - 50 characters long, contain only letters, numbers and hyphens.", k))
}

return warnings, errors
}

func CosmosEntityName(v interface{}, k string) (warnings []string, errors []error) {
value := v.(string)

if len(value) < 1 || len(value) > 255 {
errors = append(errors, fmt.Errorf(
"%q must be between 1 and 255 characters: %q", k, value))
}

return warnings, errors
}
6 changes: 5 additions & 1 deletion azurerm/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,11 @@ func Provider() terraform.ResourceProvider {
"azurerm_container_group": resourceArmContainerGroup(),
"azurerm_container_registry": resourceArmContainerRegistry(),
"azurerm_container_service": resourceArmContainerService(),
"azurerm_cosmosdb_account": resourceArmCosmosDBAccount(),
katbyte marked this conversation as resolved.
Show resolved Hide resolved
"azurerm_cosmosdb_account": resourceArmCosmosAccount(),
"azurerm_cosmos_cassandra_keyspace": resourceArmCosmosCassandraKeyspace(),
"azurerm_cosmos_mongo_database": resourceArmCosmosMongoDatabase(),
"azurerm_cosmos_sql_database": resourceArmCosmosSQLDatabase(),
"azurerm_cosmos_table": resourceArmCosmosTable(),
Copy link
Contributor

Choose a reason for hiding this comment

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

can we make all of these:

Suggested change
"azurerm_cosmos_table": resourceArmCosmosTable(),
"azurerm_cosmosdb_table": resourceArmCosmosTable(),

"azurerm_data_factory": resourceArmDataFactory(),
"azurerm_data_factory_dataset_mysql": resourceArmDataFactoryDatasetMySQL(),
"azurerm_data_factory_dataset_postgresql": resourceArmDataFactoryDatasetPostgreSQL(),
Expand Down
153 changes: 153 additions & 0 deletions azurerm/resource_arm_cosmos_cassandra_keyspace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package azurerm

import (
"fmt"
"log"

"github.com/Azure/azure-sdk-for-go/services/cosmos-db/mgmt/2015-04-08/documentdb"
"github.com/hashicorp/terraform/helper/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceArmCosmosCassandraKeyspace() *schema.Resource {
return &schema.Resource{
Create: resourceArmCosmosCassandraKeyspaceCreate,
Read: resourceArmCosmosCassandraKeyspaceRead,
Delete: resourceArmCosmosCassandraKeyspaceDelete,

Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.CosmosEntityName,
},

"resource_group_name": resourceGroupNameSchema(),

"account_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.CosmosAccountName,
},
},
}
}

func resourceArmCosmosCassandraKeyspaceCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).cosmosAccountsClient
ctx := meta.(*ArmClient).StopContext

name := d.Get("name").(string)
resourceGroup := d.Get("resource_group_name").(string)
account := d.Get("account_name").(string)

if requireResourcesToBeImported && d.IsNewResource() {
existing, err := client.GetCassandraKeyspace(ctx, resourceGroup, account, name)
if err != nil {
if !utils.ResponseWasNotFound(existing.Response) {
return fmt.Errorf("Error checking for presence of creating Cosmos Cassandra Keyspace %s (Account %s): %+v", name, account, err)
}
} else {
id, err := azure.CosmosGetIDFromResponse(existing.Response)
if err != nil {
return fmt.Errorf("Error generating import ID for Cosmos Cassandra Keyspace '%s' (Account %s)", name, account)
}

return tf.ImportAsExistsError("azurerm_cosmos_cassandra_keyspace", id)
}
}

db := documentdb.CassandraKeyspaceCreateUpdateParameters{
CassandraKeyspaceCreateUpdateProperties: &documentdb.CassandraKeyspaceCreateUpdateProperties{
Resource: &documentdb.CassandraKeyspaceResource{
ID: &name,
},
Options: map[string]*string{},
},
}

future, err := client.CreateUpdateCassandraKeyspace(ctx, resourceGroup, account, name, db)
if err != nil {
return fmt.Errorf("Error issuing create/update request for Cosmos Cassandra Keyspace %s (Account %s): %+v", name, account, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("Error waiting on create/update future for Cosmos Cassandra Keyspace %s (Account %s): %+v", name, account, err)
}

resp, err := client.GetCassandraKeyspace(ctx, resourceGroup, account, name)
if err != nil {
return fmt.Errorf("Error making get request for Cosmos Cassandra Keyspace %s (Account %s): %+v", name, account, err)
}

id, err := azure.CosmosGetIDFromResponse(resp.Response)
if err != nil {
return fmt.Errorf("Error creating Cosmos Cassandra Keyspace '%s' (Account %s) ID: %v", name, account, err)
katbyte marked this conversation as resolved.
Show resolved Hide resolved
}
d.SetId(id)

return resourceArmCosmosCassandraKeyspaceRead(d, meta)
}

func resourceArmCosmosCassandraKeyspaceRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).cosmosAccountsClient
ctx := meta.(*ArmClient).StopContext

id, err := azure.ParseCosmosKeyspaceID(d.Id())
if err != nil {
return err
}

resp, err := client.GetCassandraKeyspace(ctx, id.ResourceGroup, id.Account, id.Keyspace)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
log.Printf("[INFO] Error reading Cosmos Cassandra Keyspace %s (Account %s) - removing from state", id.Keyspace, id.Account)
d.SetId("")
return nil
}

return fmt.Errorf("Error reading Cosmos Cassandra Keyspace %s (Account %s): %+v", id.Keyspace, id.Account, err)
}

if props := resp.CassandraKeyspaceProperties; props != nil {
d.Set("name", props.ID)
d.Set("resource_group_name", id.ResourceGroup)
d.Set("account_name", id.Account)
katbyte marked this conversation as resolved.
Show resolved Hide resolved
}

return nil
}

func resourceArmCosmosCassandraKeyspaceDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).cosmosAccountsClient
ctx := meta.(*ArmClient).StopContext

id, err := azure.ParseCosmosKeyspaceID(d.Id())
if err != nil {
return err
}

future, err := client.DeleteCassandraKeyspace(ctx, id.ResourceGroup, id.Account, id.Keyspace)
if err != nil {
if !response.WasNotFound(future.Response()) {
return fmt.Errorf("Error deleting Cosmos Cassandra Keyspace %s (Account %s): %+v", id.Keyspace, id.Account, err)
}
}

err = future.WaitForCompletionRef(ctx, client.Client)
if err != nil {
return fmt.Errorf("Error waiting on delete future for Cosmos Cassandra Keyspace %s (Account %s): %+v", id.Keyspace, id.Account, err)
}

return nil
}
Loading