From a57e9050ad0ed834ef14a1c6fb2c5f914789736e Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Wed, 10 Jul 2019 14:23:09 -0700 Subject: [PATCH 1/7] start of table entity table --- .../helpers/validate/storage_table_entity.go | 0 .../authorizer_shared_key_lite_table.go | 87 ++++++ azurerm/internal/services/storage/client.go | 13 + azurerm/provider.go | 1 + azurerm/resource_arm_storage_table_entity.go | 188 +++++++++++++ .../resource_arm_storage_table_entity_test.go | 264 ++++++++++++++++++ go.sum | 3 +- .../2018-11-09/table/entities/README.md | 48 ++++ .../2018-11-09/table/entities/client.go | 25 ++ .../2018-11-09/table/entities/delete.go | 99 +++++++ .../storage/2018-11-09/table/entities/get.go | 108 +++++++ .../2018-11-09/table/entities/insert.go | 112 ++++++++ .../table/entities/insert_or_merge.go | 108 +++++++ .../table/entities/insert_or_replace.go | 108 +++++++ .../2018-11-09/table/entities/models.go | 9 + .../2018-11-09/table/entities/query.go | 155 ++++++++++ .../2018-11-09/table/entities/resource_id.go | 91 ++++++ .../2018-11-09/table/entities/version.go | 14 + vendor/modules.txt | 1 + 19 files changed, 1432 insertions(+), 2 deletions(-) create mode 100644 azurerm/helpers/validate/storage_table_entity.go create mode 100644 azurerm/internal/authorizers/authorizer_shared_key_lite_table.go create mode 100644 azurerm/resource_arm_storage_table_entity.go create mode 100644 azurerm/resource_arm_storage_table_entity_test.go create mode 100644 vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/README.md create mode 100644 vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/client.go create mode 100644 vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/delete.go create mode 100644 vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/get.go create mode 100644 vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/insert.go create mode 100644 vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/insert_or_merge.go create mode 100644 vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/insert_or_replace.go create mode 100644 vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/models.go create mode 100644 vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/query.go create mode 100644 vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/resource_id.go create mode 100644 vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/version.go diff --git a/azurerm/helpers/validate/storage_table_entity.go b/azurerm/helpers/validate/storage_table_entity.go new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/azurerm/internal/authorizers/authorizer_shared_key_lite_table.go b/azurerm/internal/authorizers/authorizer_shared_key_lite_table.go new file mode 100644 index 000000000000..8becc41cf0b6 --- /dev/null +++ b/azurerm/internal/authorizers/authorizer_shared_key_lite_table.go @@ -0,0 +1,87 @@ +package authorizers + +import ( + "net/http" + "strings" + + "github.com/Azure/go-autorest/autorest" +) + +// TODO: switch to using the version from github.com/Azure/go-autorest +// once https://github.com/Azure/go-autorest/pull/416 has been merged + +// SharedKeyLiteTableAuthorizer implements an authorization for Shared Key Lite +// this can be used for interaction with Table Storage Endpoints +type SharedKeyLiteTableAuthorizer struct { + storageAccountName string + storageAccountKey string +} + +// NewSharedKeyLiteAuthorizer crates a SharedKeyLiteAuthorizer using the given credentials +func NewSharedKeyLiteTableAuthorizer(accountName, accountKey string) *SharedKeyLiteTableAuthorizer { + return &SharedKeyLiteTableAuthorizer{ + storageAccountName: accountName, + storageAccountKey: accountKey, + } +} + +// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose +// value is "SharedKeyLite " followed by the computed key. +// This can be used for the Blob, Queue, and File Services +// +// from: https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key +// You may use Shared Key Lite authorization to authorize a request made against the +// 2009-09-19 version and later of the Blob and Queue services, +// and version 2014-02-14 and later of the File services. +func (skl *SharedKeyLiteTableAuthorizer) WithAuthorization() autorest.PrepareDecorator { + return func(p autorest.Preparer) autorest.Preparer { + return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { + r, err := p.Prepare(r) + if err != nil { + return r, err + } + + key, err := buildSharedKeyLiteTable(skl.storageAccountName, skl.storageAccountKey, r) + if err != nil { + return r, err + } + + sharedKeyHeader := formatSharedKeyLiteAuthorizationHeader(skl.storageAccountName, *key) + return autorest.Prepare(r, autorest.WithHeader(HeaderAuthorization, sharedKeyHeader)) + }) + } +} + +func buildSharedKeyLiteTable(accountName, storageAccountKey string, r *http.Request) (*string, error) { + // first ensure the relevant headers are configured + prepareHeadersForRequest(r) + + sharedKey, err := computeSharedKeyLiteTable(r.URL.String(), accountName, r.Header) + if err != nil { + return nil, err + } + + // we then need to HMAC that value + hmacdValue := hmacValue(storageAccountKey, *sharedKey) + return &hmacdValue, nil +} + +// computeSharedKeyLite computes the Shared Key Lite required for Storage Authentication +// NOTE: this function assumes that the `x-ms-date` field is set +func computeSharedKeyLiteTable(url string, accountName string, headers http.Header) (*string, error) { + dateHeader := headers.Get("x-ms-date") + canonicalizedResource, err := buildCanonicalizedResource(url, accountName) + if err != nil { + return nil, err + } + + canonicalizedString := buildCanonicalizedStringForSharedKeyLiteTable(*canonicalizedResource, dateHeader) + return &canonicalizedString, nil +} + +func buildCanonicalizedStringForSharedKeyLiteTable(canonicalizedResource, dateHeader string) string { + return strings.Join([]string{ + dateHeader, + canonicalizedResource, + }, "\n") +} diff --git a/azurerm/internal/services/storage/client.go b/azurerm/internal/services/storage/client.go index a34c42b78954..8eaa5ce92fd7 100644 --- a/azurerm/internal/services/storage/client.go +++ b/azurerm/internal/services/storage/client.go @@ -9,6 +9,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/authorizers" "github.com/tombuildsstuff/giovanni/storage/2018-11-09/file/directories" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities" ) type Client struct { @@ -68,6 +69,18 @@ func (client Client) FileShareClient(ctx context.Context, resourceGroup, account return &directoriesClient, nil } +func (client Client) TableEntityClient(ctx context.Context, resourceGroup, accountName string) (*entities.Client, error) { + accountKey, err := client.findAccountKey(ctx, resourceGroup, accountName) + if err != nil { + return nil, fmt.Errorf("Error retrieving Account Key: %s", err) + } + + storageAuth := authorizers.NewSharedKeyLiteTableAuthorizer(accountName, *accountKey) + entitiesClient := entities.New() + entitiesClient.Client.Authorizer = storageAuth + return &entitiesClient, nil +} + func (client Client) findAccountKey(ctx context.Context, resourceGroup, accountName string) (*string, error) { props, err := client.accountsClient.ListKeys(ctx, resourceGroup, accountName) if err != nil { diff --git a/azurerm/provider.go b/azurerm/provider.go index 88837a36661f..51d1483b9822 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -422,6 +422,7 @@ func Provider() terraform.ResourceProvider { "azurerm_storage_share": resourceArmStorageShare(), "azurerm_storage_share_directory": resourceArmStorageShareDirectory(), "azurerm_storage_table": resourceArmStorageTable(), + "azurerm_storage_table_entity": resourceArmStorageTableEntity(), "azurerm_stream_analytics_job": resourceArmStreamAnalyticsJob(), "azurerm_stream_analytics_function_javascript_udf": resourceArmStreamAnalyticsFunctionUDF(), "azurerm_stream_analytics_output_blob": resourceArmStreamAnalyticsOutputBlob(), diff --git a/azurerm/resource_arm_storage_table_entity.go b/azurerm/resource_arm_storage_table_entity.go new file mode 100644 index 000000000000..115df8338ea5 --- /dev/null +++ b/azurerm/resource_arm_storage_table_entity.go @@ -0,0 +1,188 @@ +package azurerm + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities" +) + +func resourceArmStorageTableEntity() *schema.Resource { + return &schema.Resource{ + Create: resourceArmStorageTableEntityCreateUpdate, + Read: resourceArmStorageTableEntityRead, + // TODO Remove this + // Update: resourceArmStorageTableEntityCreateUpdate, + Delete: resourceArmStorageTableEntityDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "table_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "storage_account_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "partition_key": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "row_key": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "metadata_level": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(entities.NoMetaData), + string(entities.MinimalMetaData), + string(entities.FullMetaData), + }, false), + }, + }, + } +} + +func resourceArmStorageTableEntityCreateUpdate(d *schema.ResourceData, meta interface{}) error { + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).storage + + accountName := d.Get("storage_account_name").(string) + tableName := d.Get("table_name").(string) + partitionKey := d.Get("partition_key").(string) + rowKey := d.Get("row_key").(string) + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group: %s", err) + } + + client, err := storageClient.TableEntityClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building Entity Client: %s", err) + } + + if requireResourcesToBeImported { + input := entities.GetEntityInput{ + PartitionKey: partitionKey, + RowKey: rowKey, + } + existing, err := client.Get(ctx, accountName, tableName, input) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing Entity (Partition Key %q / Row Key %q) (Table %q / Storage Account %q / Resource Group %q): %s", partitionKey, rowKey, tableName, accountName, *resourceGroup, err) + } + } + + if !utils.ResponseWasNotFound(existing.Response) { + id := client.GetResourceID(accountName, tableName, partitionKey, rowKey) + return tf.ImportAsExistsError("azurerm_storage_table_entity", id) + } + } + + input := entities.InsertOrMergeEntityInput{ + PartitionKey: partitionKey, + RowKey: rowKey, + } + + if _, err := client.InsertOrMerge(ctx, accountName, tableName, input); err != nil { + return fmt.Errorf("Error creating Entity (Partition Key %q / Row Key %q) (Table %q / Storage Account %q / Resource Group %q): %+v", partitionKey, rowKey, tableName, accountName, *resourceGroup, err) + } + + resourceID := client.GetResourceID(accountName, tableName, partitionKey, rowKey) + d.SetId(resourceID) + + return resourceArmStorageTableEntityRead(d, meta) +} + +func resourceArmStorageTableEntityRead(d *schema.ResourceData, meta interface{}) error { + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).storage + + id, err := entities.ParseResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Account %q: %s", id.AccountName, err) + } + if resourceGroup == nil { + log.Printf("[DEBUG] Unable to locate Resource Group for Storage Account %q - assuming removed & removing from state", id.AccountName) + d.SetId("") + return nil + } + + client, err := storageClient.TableEntityClient(ctx, *resourceGroup, id.AccountName) + if err != nil { + return fmt.Errorf("Error building Table Entity Client for Storage Account %q (Resource Group %q): %s", id.AccountName, *resourceGroup, err) + } + + input := entities.GetEntityInput{ + PartitionKey: id.PartitionKey, + RowKey: id.RowKey, + } + + _, err = client.Get(ctx, id.AccountName, id.TableName, input) + if err != nil { + return fmt.Errorf("Error retrieving Entity (Partition Key %q / Row Key %q) (Table %q / Storage Account %q / Resource Group %q): %s", id.PartitionKey, id.RowKey, id.TableName, id.AccountName, *resourceGroup, err) + } + + d.Set("storage_account_name", id.AccountName) + d.Set("table_name", id.TableName) + d.Set("partition_key", id.PartitionKey) + d.Set("row_key", id.RowKey) + + return nil +} + +func resourceArmStorageTableEntityDelete(d *schema.ResourceData, meta interface{}) error { + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).storage + + id, err := entities.ParseResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Account %q: %s", id.AccountName, err) + } + if resourceGroup == nil { + log.Printf("[DEBUG] Unable to locate Resource Group for Storage Account %q - assuming removed already", id.AccountName) + d.SetId("") + return nil + } + + client, err := storageClient.TableEntityClient(ctx, *resourceGroup, id.AccountName) + if err != nil { + return fmt.Errorf("Error building Entity Client for Storage Account %q (Resource Group %q): %s", id.AccountName, *resourceGroup, err) + } + + input := entities.DeleteEntityInput{ + PartitionKey: id.PartitionKey, + RowKey: id.RowKey, + } + + if _, err := client.Delete(ctx, id.AccountName, id.TableName, input); err != nil { + return fmt.Errorf("Error deleting Entity (Partition Key %q / Row Key %q) (Table %q / Storage Account %q / Resource Group %q): %s", id.PartitionKey, id.RowKey, id.TableName, id.AccountName, *resourceGroup, err) + } + + return nil +} diff --git a/azurerm/resource_arm_storage_table_entity_test.go b/azurerm/resource_arm_storage_table_entity_test.go new file mode 100644 index 000000000000..02d5186ef63d --- /dev/null +++ b/azurerm/resource_arm_storage_table_entity_test.go @@ -0,0 +1,264 @@ +package azurerm + +import ( + "fmt" + "net/http" + "strings" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities" +) + +func TestAccAzureRMTableEntity_basic(t *testing.T) { + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(5)) + location := testLocation() + resourceName := "azurerm_storage_table_entity.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMTableEntityDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMTableEntity_basic(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMTableEntityExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMTableEntity_requiresImport(t *testing.T) { + if !requireResourcesToBeImported { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(5)) + location := testLocation() + resourceName := "azurerm_storage_table_entity.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMTableEntityDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMTableEntity_basic(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMTableEntityExists(resourceName), + ), + }, + { + Config: testAccAzureRMTableEntity_requiresImport(ri, rs, location), + ExpectError: testRequiresImportError("azurerm_storage_table_entity"), + }, + }, + }) +} + +func TestAccAzureRMTableEntity_update(t *testing.T) { + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(5)) + location := testLocation() + resourceName := "azurerm_storage_table_entity.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMTableEntityDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMTableEntity_basic(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMTableEntityExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMTableEntity_updated(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMTableEntityExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMTableEntityExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + tableName := rs.Primary.Attributes["table_name"] + accountName := rs.Primary.Attributes["storage_account_name"] + partitionKey := rs.Primary.Attributes["parititon_key"] + rowKey := rs.Primary.Attributes["row_key"] + + storageClient := testAccProvider.Meta().(*ArmClient).storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) + if err != nil { + return fmt.Errorf("Error finding Resource Group: %s", err) + } + + client, err := storageClient.TableEntityClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building Table Entity Client: %s", err) + } + + input := entities.GetEntityInput{ + PartitionKey: partitionKey, + RowKey: rowKey, + } + resp, err := client.Get(ctx, accountName, tableName, input) + if err != nil { + return fmt.Errorf("Bad: Get on Table EntityClient: %+v", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: Entity (Partition Key %q / Row Key %q) (Table %q / Account %q / Resource Group %q) does not exist", partitionKey, rowKey, tableName, accountName, *resourceGroup) + } + + return nil + } +} + +func testCheckAzureRMTableEntityDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_storage_table_entity" { + continue + } + + tableName := rs.Primary.Attributes["share_name"] + accountName := rs.Primary.Attributes["storage_account_name"] + partitionKey := rs.Primary.Attributes["parititon_key"] + rowKey := rs.Primary.Attributes["row_key"] + + storageClient := testAccProvider.Meta().(*ArmClient).storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) + if err != nil { + return fmt.Errorf("Error finding Resource Group: %s", err) + } + + // not found, the account's gone + if resourceGroup == nil { + return nil + } + + client, err := storageClient.TableEntityClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building TableEntity Client: %s", err) + } + + input := entities.GetEntityInput{ + PartitionKey: partitionKey, + RowKey: rowKey, + } + resp, err := client.Get(ctx, accountName, tableName, input) + if err != nil { + return fmt.Errorf("Bad: Get on Table Entity: %+v", err) + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("Table Entity still exists:\n%#v", resp) + } + } + + return nil +} + +func testAccAzureRMTableEntity_basic(rInt int, rString string, location string) string { + template := testAccAzureRMTableEntity_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_table_entity" "test" { + storage_account_name = "${azurerm_storage_account.test.name}" + table_name = "${azurerm_storage_table.test.name}" + + partition_key = "test_partition%d" + row_key = "test_row%d" +} +`, template, rInt, rInt) +} + +func testAccAzureRMTableEntity_requiresImport(rInt int, rString string, location string) string { + template := testAccAzureRMTableEntity_basic(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_table_entity" "test" { + storage_account_name = "${azurerm_storage_account.test.name}" + table_name = "${azurerm_storage_table.test.name}" + + partition_key = "test_partition%d" + row_key = "test_row%d" +} +`, template, rInt, rInt) +} + +func testAccAzureRMTableEntity_updated(rInt int, rString string, location string) string { + template := testAccAzureRMTableEntity_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_table_entity" "test" { + storage_account_name = "${azurerm_storage_account.test.name}" + table_name = "${azurerm_storage_table.test.name}" + + partition_key = "test_partition%d" + row_key = "test_row%d" +} +`, template, rInt, rInt) +} + +func testAccAzureRMTableEntity_template(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestsa%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_storage_table" "test" { + name = "acctestst%d" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" +} +`, rInt, location, rString, rInt) +} diff --git a/go.sum b/go.sum index b4c3607f7ed5..b941ea35f348 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,7 @@ github.com/Azure/go-autorest/autorest/azure/cli v0.1.0 h1:YTtBrcb6mhA+PoSW8WxFDo github.com/Azure/go-autorest/autorest/azure/cli v0.1.0/go.mod h1:Dk8CUAt/b/PzkfeRsWzVG9Yj3ps8mS8ECztu43rdU8U= github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0 h1:Kx+AUU2Te+A3JIyYn6Dfs+cFgx5XorQKuIXrZGoq/SI= github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/to v0.2.0 h1:nQOZzFCudTh+TvquAtCRjM01VEYx85e9qbwt5ncW4L8= github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= @@ -91,8 +92,6 @@ github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx2 github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k= github.com/census-instrumentation/opencensus-proto v0.1.0-0.20181214143942-ba49f56771b8/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.1.0 h1:VwZ9smxzX8u14/125wHIX7ARV+YhR+L4JADswwxWK0Y= -github.com/census-instrumentation/opencensus-proto v0.1.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= diff --git a/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/README.md b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/README.md new file mode 100644 index 000000000000..06c2cb8a5ce1 --- /dev/null +++ b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/README.md @@ -0,0 +1,48 @@ +## Table Storage Entities SDK for API version 2018-11-09 + +This package allows you to interact with the Entities Table Storage API + +### Supported Authorizers + +* SharedKeyLite (Table) + +### Example Usage + +```go +package main + +import ( + "context" + "fmt" + "time" + + "github.com/Azure/go-autorest/autorest" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities" +) + +func Example() error { + accountName := "storageaccount1" + storageAccountKey := "ABC123...." + tableName := "mytable" + + storageAuth := autorest.NewSharedKeyLiteTableAuthorizer(accountName, storageAccountKey) + entitiesClient := entities.New() + entitiesClient.Client.Authorizer = storageAuth + + ctx := context.TODO() + input := entities.InsertEntityInput{ + PartitionKey: "abc", + RowKey: "123", + MetaDataLevel: entities.NoMetaData, + Entity: map[string]interface{}{ + "title": "Don't Kill My Vibe", + "artist": "Sigrid", + }, + } + if _, err := entitiesClient.Insert(ctx, accountName, tableName, input); err != nil { + return fmt.Errorf("Error creating Entity: %s", err) + } + + return nil +} +``` \ No newline at end of file diff --git a/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/client.go b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/client.go new file mode 100644 index 000000000000..17e9d759fb9e --- /dev/null +++ b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/client.go @@ -0,0 +1,25 @@ +package entities + +import ( + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" +) + +// Client is the base client for Table Storage Shares. +type Client struct { + autorest.Client + BaseURI string +} + +// New creates an instance of the Client client. +func New() Client { + return NewWithEnvironment(azure.PublicCloud) +} + +// NewWithEnvironment creates an instance of the Client client. +func NewWithEnvironment(environment azure.Environment) Client { + return Client{ + Client: autorest.NewClientWithUserAgent(UserAgent()), + BaseURI: environment.StorageEndpointSuffix, + } +} diff --git a/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/delete.go b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/delete.go new file mode 100644 index 000000000000..83e9188e5e9e --- /dev/null +++ b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/delete.go @@ -0,0 +1,99 @@ +package entities + +import ( + "context" + "net/http" + + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/go-autorest/autorest/validation" + "github.com/tombuildsstuff/giovanni/storage/internal/endpoints" +) + +type DeleteEntityInput struct { + // When inserting an entity into a table, you must specify values for the PartitionKey and RowKey system properties. + // Together, these properties form the primary key and must be unique within the table. + // Both the PartitionKey and RowKey values must be string values; each key value may be up to 64 KB in size. + // If you are using an integer value for the key value, you should convert the integer to a fixed-width string, + // because they are canonically sorted. For example, you should convert the value 1 to 0000001 to ensure proper sorting. + RowKey string + PartitionKey string +} + +// Delete deletes an existing entity in a table. +func (client Client) Delete(ctx context.Context, accountName, tableName string, input DeleteEntityInput) (result autorest.Response, err error) { + if accountName == "" { + return result, validation.NewError("entities.Client", "Delete", "`accountName` cannot be an empty string.") + } + if tableName == "" { + return result, validation.NewError("entities.Client", "Delete", "`tableName` cannot be an empty string.") + } + if input.PartitionKey == "" { + return result, validation.NewError("entities.Client", "Delete", "`input.PartitionKey` cannot be an empty string.") + } + if input.RowKey == "" { + return result, validation.NewError("entities.Client", "Delete", "`input.RowKey` cannot be an empty string.") + } + + req, err := client.DeletePreparer(ctx, accountName, tableName, input) + if err != nil { + err = autorest.NewErrorWithError(err, "entities.Client", "Delete", nil, "Failure preparing request") + return + } + + resp, err := client.DeleteSender(req) + if err != nil { + result = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "entities.Client", "Delete", resp, "Failure sending request") + return + } + + result, err = client.DeleteResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "entities.Client", "Delete", resp, "Failure responding to request") + return + } + + return +} + +// DeletePreparer prepares the Delete request. +func (client Client) DeletePreparer(ctx context.Context, accountName, tableName string, input DeleteEntityInput) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "tableName": autorest.Encode("path", tableName), + "partitionKey": autorest.Encode("path", input.PartitionKey), + "rowKey": autorest.Encode("path", input.RowKey), + } + + headers := map[string]interface{}{ + // TODO: support for eTags + "If-Match": "*", + } + + preparer := autorest.CreatePreparer( + autorest.AsDelete(), + autorest.WithBaseURL(endpoints.GetTableEndpoint(client.BaseURI, accountName)), + autorest.WithPathParameters("/{tableName}(PartitionKey='{partitionKey}', RowKey='{rowKey}')", pathParameters), + autorest.WithHeaders(headers)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// DeleteSender sends the Delete request. The method will close the +// http.Response Body if it receives an error. +func (client Client) DeleteSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + azure.DoRetryWithRegistration(client.Client)) +} + +// DeleteResponder handles the response to the Delete request. The method always +// closes the http.Response Body. +func (client Client) DeleteResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusNoContent), + autorest.ByClosing()) + result = autorest.Response{Response: resp} + + return +} diff --git a/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/get.go b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/get.go new file mode 100644 index 000000000000..bdb40180b54d --- /dev/null +++ b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/get.go @@ -0,0 +1,108 @@ +package entities + +import ( + "context" + "fmt" + "net/http" + + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/go-autorest/autorest/validation" + "github.com/tombuildsstuff/giovanni/storage/internal/endpoints" +) + +type GetEntityInput struct { + PartitionKey string + RowKey string + + // The Level of MetaData which should be returned + MetaDataLevel MetaDataLevel +} + +type GetEntityResult struct { + autorest.Response + + Entity map[string]interface{} +} + +// Get queries entities in a table and includes the $filter and $select options. +func (client Client) Get(ctx context.Context, accountName, tableName string, input GetEntityInput) (result GetEntityResult, err error) { + if accountName == "" { + return result, validation.NewError("entities.Client", "Get", "`accountName` cannot be an empty string.") + } + if tableName == "" { + return result, validation.NewError("entities.Client", "Get", "`tableName` cannot be an empty string.") + } + if input.PartitionKey == "" { + return result, validation.NewError("entities.Client", "Get", "`input.PartitionKey` cannot be an empty string.") + } + if input.RowKey == "" { + return result, validation.NewError("entities.Client", "Get", "`input.RowKey` cannot be an empty string.") + } + + req, err := client.GetPreparer(ctx, accountName, tableName, input) + if err != nil { + err = autorest.NewErrorWithError(err, "entities.Client", "Get", nil, "Failure preparing request") + return + } + + resp, err := client.GetSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "entities.Client", "Get", resp, "Failure sending request") + return + } + + result, err = client.GetResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "entities.Client", "Get", resp, "Failure responding to request") + return + } + + return +} + +// GetPreparer prepares the Get request. +func (client Client) GetPreparer(ctx context.Context, accountName, tableName string, input GetEntityInput) (*http.Request, error) { + + pathParameters := map[string]interface{}{ + "tableName": autorest.Encode("path", tableName), + "partitionKey": autorest.Encode("path", input.PartitionKey), + "rowKey": autorest.Encode("path", input.RowKey), + } + + headers := map[string]interface{}{ + "x-ms-version": APIVersion, + "Accept": fmt.Sprintf("application/json;odata=%s", input.MetaDataLevel), + "DataServiceVersion": "3.0;NetFx", + "MaxDataServiceVersion": "3.0;NetFx", + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(endpoints.GetTableEndpoint(client.BaseURI, accountName)), + autorest.WithPathParameters("/{tableName}(PartitionKey='{partitionKey}',RowKey='{rowKey}')", pathParameters), + autorest.WithHeaders(headers)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// GetSender sends the Get request. The method will close the +// http.Response Body if it receives an error. +func (client Client) GetSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + azure.DoRetryWithRegistration(client.Client)) +} + +// GetResponder handles the response to the Get request. The method always +// closes the http.Response Body. +func (client Client) GetResponder(resp *http.Response) (result GetEntityResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result.Entity), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + + return +} diff --git a/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/insert.go b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/insert.go new file mode 100644 index 000000000000..92b05cef5499 --- /dev/null +++ b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/insert.go @@ -0,0 +1,112 @@ +package entities + +import ( + "context" + "fmt" + "net/http" + + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/go-autorest/autorest/validation" + "github.com/tombuildsstuff/giovanni/storage/internal/endpoints" +) + +type InsertEntityInput struct { + // The level of MetaData provided for this Entity + MetaDataLevel MetaDataLevel + + // The Entity which should be inserted, by default all values are strings + // To explicitly type a property, specify the appropriate OData data type by setting + // the m:type attribute within the property definition + Entity map[string]interface{} + + // When inserting an entity into a table, you must specify values for the PartitionKey and RowKey system properties. + // Together, these properties form the primary key and must be unique within the table. + // Both the PartitionKey and RowKey values must be string values; each key value may be up to 64 KB in size. + // If you are using an integer value for the key value, you should convert the integer to a fixed-width string, + // because they are canonically sorted. For example, you should convert the value 1 to 0000001 to ensure proper sorting. + RowKey string + PartitionKey string +} + +// Insert inserts a new entity into a table. +func (client Client) Insert(ctx context.Context, accountName, tableName string, input InsertEntityInput) (result autorest.Response, err error) { + if accountName == "" { + return result, validation.NewError("entities.Client", "Insert", "`accountName` cannot be an empty string.") + } + if tableName == "" { + return result, validation.NewError("entities.Client", "Insert", "`tableName` cannot be an empty string.") + } + if input.PartitionKey == "" { + return result, validation.NewError("entities.Client", "Insert", "`input.PartitionKey` cannot be an empty string.") + } + if input.RowKey == "" { + return result, validation.NewError("entities.Client", "Insert", "`input.RowKey` cannot be an empty string.") + } + + req, err := client.InsertPreparer(ctx, accountName, tableName, input) + if err != nil { + err = autorest.NewErrorWithError(err, "entities.Client", "Insert", nil, "Failure preparing request") + return + } + + resp, err := client.InsertSender(req) + if err != nil { + result = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "entities.Client", "Insert", resp, "Failure sending request") + return + } + + result, err = client.InsertResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "entities.Client", "Insert", resp, "Failure responding to request") + return + } + + return +} + +// InsertPreparer prepares the Insert request. +func (client Client) InsertPreparer(ctx context.Context, accountName, tableName string, input InsertEntityInput) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "tableName": autorest.Encode("path", tableName), + } + + headers := map[string]interface{}{ + "x-ms-version": APIVersion, + "Accept": fmt.Sprintf("application/json;odata=%s", input.MetaDataLevel), + "Prefer": "return-no-content", + } + + input.Entity["PartitionKey"] = input.PartitionKey + input.Entity["RowKey"] = input.RowKey + + preparer := autorest.CreatePreparer( + autorest.AsContentType("application/json"), + autorest.AsPost(), + autorest.WithBaseURL(endpoints.GetTableEndpoint(client.BaseURI, accountName)), + autorest.WithPathParameters("/{tableName}", pathParameters), + autorest.WithJSON(input.Entity), + autorest.WithHeaders(headers)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// InsertSender sends the Insert request. The method will close the +// http.Response Body if it receives an error. +func (client Client) InsertSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + azure.DoRetryWithRegistration(client.Client)) +} + +// InsertResponder handles the response to the Insert request. The method always +// closes the http.Response Body. +func (client Client) InsertResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusNoContent), + autorest.ByClosing()) + result = autorest.Response{Response: resp} + + return +} diff --git a/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/insert_or_merge.go b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/insert_or_merge.go new file mode 100644 index 000000000000..1fb4ed3df164 --- /dev/null +++ b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/insert_or_merge.go @@ -0,0 +1,108 @@ +package entities + +import ( + "context" + "net/http" + + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/go-autorest/autorest/validation" + "github.com/tombuildsstuff/giovanni/storage/internal/endpoints" +) + +type InsertOrMergeEntityInput struct { + // The Entity which should be inserted, by default all values are strings + // To explicitly type a property, specify the appropriate OData data type by setting + // the m:type attribute within the property definition + Entity map[string]interface{} + + // When inserting an entity into a table, you must specify values for the PartitionKey and RowKey system properties. + // Together, these properties form the primary key and must be unique within the table. + // Both the PartitionKey and RowKey values must be string values; each key value may be up to 64 KB in size. + // If you are using an integer value for the key value, you should convert the integer to a fixed-width string, + // because they are canonically sorted. For example, you should convert the value 1 to 0000001 to ensure proper sorting. + RowKey string + PartitionKey string +} + +// InsertOrMerge updates an existing entity or inserts a new entity if it does not exist in the table. +// Because this operation can insert or update an entity, it is also known as an upsert operation. +func (client Client) InsertOrMerge(ctx context.Context, accountName, tableName string, input InsertOrMergeEntityInput) (result autorest.Response, err error) { + if accountName == "" { + return result, validation.NewError("entities.Client", "InsertOrMerge", "`accountName` cannot be an empty string.") + } + if tableName == "" { + return result, validation.NewError("entities.Client", "InsertOrMerge", "`tableName` cannot be an empty string.") + } + if input.PartitionKey == "" { + return result, validation.NewError("entities.Client", "InsertOrMerge", "`input.PartitionKey` cannot be an empty string.") + } + if input.RowKey == "" { + return result, validation.NewError("entities.Client", "InsertOrMerge", "`input.RowKey` cannot be an empty string.") + } + + req, err := client.InsertOrMergePreparer(ctx, accountName, tableName, input) + if err != nil { + err = autorest.NewErrorWithError(err, "entities.Client", "InsertOrMerge", nil, "Failure preparing request") + return + } + + resp, err := client.InsertOrMergeSender(req) + if err != nil { + result = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "entities.Client", "InsertOrMerge", resp, "Failure sending request") + return + } + + result, err = client.InsertOrMergeResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "entities.Client", "InsertOrMerge", resp, "Failure responding to request") + return + } + + return +} + +// InsertOrMergePreparer prepares the InsertOrMerge request. +func (client Client) InsertOrMergePreparer(ctx context.Context, accountName, tableName string, input InsertOrMergeEntityInput) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "tableName": autorest.Encode("path", tableName), + "partitionKey": autorest.Encode("path", input.PartitionKey), + "rowKey": autorest.Encode("path", input.RowKey), + } + + headers := map[string]interface{}{ + "x-ms-version": APIVersion, + "Accept": "application/json", + "Prefer": "return-no-content", + } + + preparer := autorest.CreatePreparer( + autorest.AsContentType("application/json"), + autorest.AsMerge(), + autorest.WithBaseURL(endpoints.GetTableEndpoint(client.BaseURI, accountName)), + autorest.WithPathParameters("/{tableName}(PartitionKey='{partitionKey}', RowKey='{rowKey}')", pathParameters), + autorest.WithJSON(input.Entity), + autorest.WithHeaders(headers)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// InsertOrMergeSender sends the InsertOrMerge request. The method will close the +// http.Response Body if it receives an error. +func (client Client) InsertOrMergeSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + azure.DoRetryWithRegistration(client.Client)) +} + +// InsertOrMergeResponder handles the response to the InsertOrMerge request. The method always +// closes the http.Response Body. +func (client Client) InsertOrMergeResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusNoContent), + autorest.ByClosing()) + result = autorest.Response{Response: resp} + + return +} diff --git a/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/insert_or_replace.go b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/insert_or_replace.go new file mode 100644 index 000000000000..036ba5de5fe7 --- /dev/null +++ b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/insert_or_replace.go @@ -0,0 +1,108 @@ +package entities + +import ( + "context" + "net/http" + + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/go-autorest/autorest/validation" + "github.com/tombuildsstuff/giovanni/storage/internal/endpoints" +) + +type InsertOrReplaceEntityInput struct { + // The Entity which should be inserted, by default all values are strings + // To explicitly type a property, specify the appropriate OData data type by setting + // the m:type attribute within the property definition + Entity map[string]interface{} + + // When inserting an entity into a table, you must specify values for the PartitionKey and RowKey system properties. + // Together, these properties form the primary key and must be unique within the table. + // Both the PartitionKey and RowKey values must be string values; each key value may be up to 64 KB in size. + // If you are using an integer value for the key value, you should convert the integer to a fixed-width string, + // because they are canonically sorted. For example, you should convert the value 1 to 0000001 to ensure proper sorting. + RowKey string + PartitionKey string +} + +// InsertOrReplace replaces an existing entity or inserts a new entity if it does not exist in the table. +// Because this operation can insert or update an entity, it is also known as an upsert operation. +func (client Client) InsertOrReplace(ctx context.Context, accountName, tableName string, input InsertOrReplaceEntityInput) (result autorest.Response, err error) { + if accountName == "" { + return result, validation.NewError("entities.Client", "InsertOrReplace", "`accountName` cannot be an empty string.") + } + if tableName == "" { + return result, validation.NewError("entities.Client", "InsertOrReplace", "`tableName` cannot be an empty string.") + } + if input.PartitionKey == "" { + return result, validation.NewError("entities.Client", "InsertOrReplace", "`input.PartitionKey` cannot be an empty string.") + } + if input.RowKey == "" { + return result, validation.NewError("entities.Client", "InsertOrReplace", "`input.RowKey` cannot be an empty string.") + } + + req, err := client.InsertOrReplacePreparer(ctx, accountName, tableName, input) + if err != nil { + err = autorest.NewErrorWithError(err, "entities.Client", "InsertOrReplace", nil, "Failure preparing request") + return + } + + resp, err := client.InsertOrReplaceSender(req) + if err != nil { + result = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "entities.Client", "InsertOrReplace", resp, "Failure sending request") + return + } + + result, err = client.InsertOrReplaceResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "entities.Client", "InsertOrReplace", resp, "Failure responding to request") + return + } + + return +} + +// InsertOrReplacePreparer prepares the InsertOrReplace request. +func (client Client) InsertOrReplacePreparer(ctx context.Context, accountName, tableName string, input InsertOrReplaceEntityInput) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "tableName": autorest.Encode("path", tableName), + "partitionKey": autorest.Encode("path", input.PartitionKey), + "rowKey": autorest.Encode("path", input.RowKey), + } + + headers := map[string]interface{}{ + "x-ms-version": APIVersion, + "Accept": "application/json", + "Prefer": "return-no-content", + } + + preparer := autorest.CreatePreparer( + autorest.AsContentType("application/json"), + autorest.AsMerge(), + autorest.WithBaseURL(endpoints.GetTableEndpoint(client.BaseURI, accountName)), + autorest.WithPathParameters("/{tableName}(PartitionKey='{partitionKey}', RowKey='{rowKey}')", pathParameters), + autorest.WithJSON(input.Entity), + autorest.WithHeaders(headers)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// InsertOrReplaceSender sends the InsertOrReplace request. The method will close the +// http.Response Body if it receives an error. +func (client Client) InsertOrReplaceSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + azure.DoRetryWithRegistration(client.Client)) +} + +// InsertOrReplaceResponder handles the response to the InsertOrReplace request. The method always +// closes the http.Response Body. +func (client Client) InsertOrReplaceResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusNoContent), + autorest.ByClosing()) + result = autorest.Response{Response: resp} + + return +} diff --git a/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/models.go b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/models.go new file mode 100644 index 000000000000..e3c6cccb59ce --- /dev/null +++ b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/models.go @@ -0,0 +1,9 @@ +package entities + +type MetaDataLevel string + +var ( + NoMetaData MetaDataLevel = "nometadata" + MinimalMetaData MetaDataLevel = "minimalmetadata" + FullMetaData MetaDataLevel = "fullmetadata" +) diff --git a/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/query.go b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/query.go new file mode 100644 index 000000000000..a768b830cd82 --- /dev/null +++ b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/query.go @@ -0,0 +1,155 @@ +package entities + +import ( + "context" + "fmt" + "net/http" + "strings" + + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/go-autorest/autorest/validation" + "github.com/tombuildsstuff/giovanni/storage/internal/endpoints" +) + +type QueryEntitiesInput struct { + // An optional OData filter + Filter *string + + // An optional comma-separated + PropertyNamesToSelect *[]string + + PartitionKey string + RowKey string + + // The Level of MetaData which should be returned + MetaDataLevel MetaDataLevel + + // The Next Partition Key used to load data from a previous point + NextPartitionKey *string + + // The Next Row Key used to load data from a previous point + NextRowKey *string +} + +type QueryEntitiesResult struct { + autorest.Response + + NextPartitionKey string + NextRowKey string + + MetaData string `json:"odata.metadata,omitempty"` + Entities []map[string]interface{} `json:"value"` +} + +// Query queries entities in a table and includes the $filter and $select options. +func (client Client) Query(ctx context.Context, accountName, tableName string, input QueryEntitiesInput) (result QueryEntitiesResult, err error) { + if accountName == "" { + return result, validation.NewError("entities.Client", "Query", "`accountName` cannot be an empty string.") + } + if tableName == "" { + return result, validation.NewError("entities.Client", "Query", "`tableName` cannot be an empty string.") + } + + req, err := client.QueryPreparer(ctx, accountName, tableName, input) + if err != nil { + err = autorest.NewErrorWithError(err, "entities.Client", "Query", nil, "Failure preparing request") + return + } + + resp, err := client.QuerySender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "entities.Client", "Query", resp, "Failure sending request") + return + } + + result, err = client.QueryResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "entities.Client", "Query", resp, "Failure responding to request") + return + } + + return +} + +// QueryPreparer prepares the Query request. +func (client Client) QueryPreparer(ctx context.Context, accountName, tableName string, input QueryEntitiesInput) (*http.Request, error) { + + pathParameters := map[string]interface{}{ + "tableName": autorest.Encode("path", tableName), + "additionalParameters": "", + } + + //PartitionKey='',RowKey='' + additionalParams := make([]string, 0) + if input.PartitionKey != "" { + additionalParams = append(additionalParams, fmt.Sprintf("PartitionKey='%s'", input.PartitionKey)) + } + if input.RowKey != "" { + additionalParams = append(additionalParams, fmt.Sprintf("RowKey='%s'", input.RowKey)) + } + if len(additionalParams) > 0 { + pathParameters["additionalParameters"] = autorest.Encode("path", strings.Join(additionalParams, ",")) + } + + queryParameters := map[string]interface{}{} + + if input.Filter != nil { + queryParameters["filter"] = autorest.Encode("query", input.Filter) + } + + if input.PropertyNamesToSelect != nil { + queryParameters["$select"] = autorest.Encode("query", strings.Join(*input.PropertyNamesToSelect, ",")) + } + + if input.NextPartitionKey != nil { + queryParameters["NextPartitionKey"] = *input.NextPartitionKey + } + + if input.NextRowKey != nil { + queryParameters["NextRowKey"] = *input.NextRowKey + } + + headers := map[string]interface{}{ + "x-ms-version": APIVersion, + "Accept": fmt.Sprintf("application/json;odata=%s", input.MetaDataLevel), + "DataServiceVersion": "3.0;NetFx", + "MaxDataServiceVersion": "3.0;NetFx", + } + + // GET /myaccount/Customers()?$filter=(Rating%20ge%203)%20and%20(Rating%20le%206)&$select=PartitionKey,RowKey,Address,CustomerSince + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(endpoints.GetTableEndpoint(client.BaseURI, accountName)), + autorest.WithPathParameters("/{tableName}({additionalParameters})", pathParameters), + autorest.WithQueryParameters(queryParameters), + autorest.WithHeaders(headers)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// QuerySender sends the Query request. The method will close the +// http.Response Body if it receives an error. +func (client Client) QuerySender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + azure.DoRetryWithRegistration(client.Client)) +} + +// QueryResponder handles the response to the Query request. The method always +// closes the http.Response Body. +func (client Client) QueryResponder(resp *http.Response) (result QueryEntitiesResult, err error) { + if resp != nil && resp.Header != nil { + result.NextPartitionKey = resp.Header.Get("x-ms-continuation-NextPartitionKey") + result.NextRowKey = resp.Header.Get("x-ms-continuation-NextRowKey") + } + + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + + return +} diff --git a/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/resource_id.go b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/resource_id.go new file mode 100644 index 000000000000..249671dc91bf --- /dev/null +++ b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/resource_id.go @@ -0,0 +1,91 @@ +package entities + +import ( + "fmt" + "net/url" + "strings" + + "github.com/tombuildsstuff/giovanni/storage/internal/endpoints" +) + +// GetResourceID returns the Resource ID for the given Entity +// This can be useful when, for example, you're using this as a unique identifier +func (client Client) GetResourceID(accountName, tableName, partitionKey, rowKey string) string { + domain := endpoints.GetTableEndpoint(client.BaseURI, accountName) + return fmt.Sprintf("%s/%s(PartitionKey='%s',RowKey='%s')", domain, tableName, partitionKey, rowKey) +} + +type ResourceID struct { + AccountName string + TableName string + PartitionKey string + RowKey string +} + +// ParseResourceID parses the specified Resource ID and returns an object which +// can be used to look up the specified Entity within the specified Table +func ParseResourceID(id string) (*ResourceID, error) { + // example: https://account1.table.core.chinacloudapi.cn/table1(PartitionKey='partition1',RowKey='row1') + if id == "" { + return nil, fmt.Errorf("`id` was empty") + } + + uri, err := url.Parse(id) + if err != nil { + return nil, fmt.Errorf("Error parsing ID as a URL: %s", err) + } + + accountName, err := endpoints.GetAccountNameFromEndpoint(uri.Host) + if err != nil { + return nil, fmt.Errorf("Error parsing Account Name: %s", err) + } + + // assume there a `Table('')` + path := strings.TrimPrefix(uri.Path, "/") + if !strings.Contains(uri.Path, "(") || !strings.HasSuffix(uri.Path, ")") { + return nil, fmt.Errorf("Expected the Table Name to be in the format `tables(PartitionKey='',RowKey='')` but got %q", path) + } + + // NOTE: honestly this could probably be a RegEx, but this seemed like the simplest way to + // allow these two fields to be specified in either order + indexOfBracket := strings.IndexByte(path, '(') + tableName := path[0:indexOfBracket] + + // trim off the brackets + temp := strings.TrimPrefix(path, fmt.Sprintf("%s(", tableName)) + temp = strings.TrimSuffix(temp, ")") + + dictionary := strings.Split(temp, ",") + partitionKey := "" + rowKey := "" + for _, v := range dictionary { + split := strings.Split(v, "=") + if len(split) != 2 { + return nil, fmt.Errorf("Expected 2 segments but got %d for %q", len(split), v) + } + + key := split[0] + value := strings.TrimSuffix(strings.TrimPrefix(split[1], "'"), "'") + if strings.EqualFold(key, "PartitionKey") { + partitionKey = value + } else if strings.EqualFold(key, "RowKey") { + rowKey = value + } else { + return nil, fmt.Errorf("Unexpected Key %q", key) + } + } + + if partitionKey == "" { + return nil, fmt.Errorf("Expected a PartitionKey but didn't get one") + } + if rowKey == "" { + return nil, fmt.Errorf("Expected a RowKey but didn't get one") + } + + return &ResourceID{ + AccountName: *accountName, + TableName: tableName, + PartitionKey: partitionKey, + RowKey: rowKey, + }, nil +} diff --git a/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/version.go b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/version.go new file mode 100644 index 000000000000..0914b240205d --- /dev/null +++ b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities/version.go @@ -0,0 +1,14 @@ +package entities + +import ( + "fmt" + + "github.com/tombuildsstuff/giovanni/version" +) + +// APIVersion is the version of the API used for all Storage API Operations +const APIVersion = "2018-11-09" + +func UserAgent() string { + return fmt.Sprintf("tombuildsstuff/giovanni/%s storage/%s", version.Number, APIVersion) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index f72e9774f829..c07457a4df33 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -340,6 +340,7 @@ github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/valida github.com/terraform-providers/terraform-provider-azuread/version # github.com/tombuildsstuff/giovanni v0.2.0 github.com/tombuildsstuff/giovanni/storage/2018-11-09/file/directories +github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities github.com/tombuildsstuff/giovanni/storage/internal/endpoints github.com/tombuildsstuff/giovanni/storage/internal/metadata github.com/tombuildsstuff/giovanni/version From 2b40e4081e6bacc350981fe5708f92a062f0550d Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Thu, 11 Jul 2019 11:14:15 -0700 Subject: [PATCH 2/7] Finalizing storage_table_entity --- .../helpers/validate/storage_table_entity.go | 0 azurerm/resource_arm_storage_table_entity.go | 37 +++++---- .../resource_arm_storage_table_entity_test.go | 19 ++++- website/azurerm.erb | 4 + website/docs/r/storage_table.html.markdown | 2 +- .../docs/r/storage_table_entity.html.markdown | 79 +++++++++++++++++++ 6 files changed, 121 insertions(+), 20 deletions(-) delete mode 100644 azurerm/helpers/validate/storage_table_entity.go create mode 100644 website/docs/r/storage_table_entity.html.markdown diff --git a/azurerm/helpers/validate/storage_table_entity.go b/azurerm/helpers/validate/storage_table_entity.go deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/azurerm/resource_arm_storage_table_entity.go b/azurerm/resource_arm_storage_table_entity.go index 115df8338ea5..71061669d0c3 100644 --- a/azurerm/resource_arm_storage_table_entity.go +++ b/azurerm/resource_arm_storage_table_entity.go @@ -5,7 +5,6 @@ import ( "log" "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" "github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities" @@ -15,8 +14,7 @@ func resourceArmStorageTableEntity() *schema.Resource { return &schema.Resource{ Create: resourceArmStorageTableEntityCreateUpdate, Read: resourceArmStorageTableEntityRead, - // TODO Remove this - // Update: resourceArmStorageTableEntityCreateUpdate, + Update: resourceArmStorageTableEntityCreateUpdate, Delete: resourceArmStorageTableEntityDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, @@ -43,15 +41,12 @@ func resourceArmStorageTableEntity() *schema.Resource { Required: true, ForceNew: true, }, - "metadata_level": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - string(entities.NoMetaData), - string(entities.MinimalMetaData), - string(entities.FullMetaData), - }, false), + "entity": { + Type: schema.TypeMap, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, }, } @@ -97,6 +92,7 @@ func resourceArmStorageTableEntityCreateUpdate(d *schema.ResourceData, meta inte input := entities.InsertOrMergeEntityInput{ PartitionKey: partitionKey, RowKey: rowKey, + Entity: d.Get("entity").(map[string]interface{}), } if _, err := client.InsertOrMerge(ctx, accountName, tableName, input); err != nil { @@ -134,11 +130,12 @@ func resourceArmStorageTableEntityRead(d *schema.ResourceData, meta interface{}) } input := entities.GetEntityInput{ - PartitionKey: id.PartitionKey, - RowKey: id.RowKey, + PartitionKey: id.PartitionKey, + RowKey: id.RowKey, + MetaDataLevel: entities.NoMetaData, } - _, err = client.Get(ctx, id.AccountName, id.TableName, input) + result, err := client.Get(ctx, id.AccountName, id.TableName, input) if err != nil { return fmt.Errorf("Error retrieving Entity (Partition Key %q / Row Key %q) (Table %q / Storage Account %q / Resource Group %q): %s", id.PartitionKey, id.RowKey, id.TableName, id.AccountName, *resourceGroup, err) } @@ -147,6 +144,7 @@ func resourceArmStorageTableEntityRead(d *schema.ResourceData, meta interface{}) d.Set("table_name", id.TableName) d.Set("partition_key", id.PartitionKey) d.Set("row_key", id.RowKey) + d.Set("entity", flattenEntity(result.Entity)) return nil } @@ -186,3 +184,12 @@ func resourceArmStorageTableEntityDelete(d *schema.ResourceData, meta interface{ return nil } + +// The api returns extra information that we already have. We'll remove it here before setting it in state. +func flattenEntity(entity map[string]interface{}) map[string]interface{} { + delete(entity, "PartitionKey") + delete(entity, "RowKey") + delete(entity, "Timestamp") + + return entity +} diff --git a/azurerm/resource_arm_storage_table_entity_test.go b/azurerm/resource_arm_storage_table_entity_test.go index 02d5186ef63d..35c68685cab5 100644 --- a/azurerm/resource_arm_storage_table_entity_test.go +++ b/azurerm/resource_arm_storage_table_entity_test.go @@ -116,7 +116,7 @@ func testCheckAzureRMTableEntityExists(resourceName string) resource.TestCheckFu tableName := rs.Primary.Attributes["table_name"] accountName := rs.Primary.Attributes["storage_account_name"] - partitionKey := rs.Primary.Attributes["parititon_key"] + partitionKey := rs.Primary.Attributes["partition_key"] rowKey := rs.Primary.Attributes["row_key"] storageClient := testAccProvider.Meta().(*ArmClient).storage @@ -133,8 +133,9 @@ func testCheckAzureRMTableEntityExists(resourceName string) resource.TestCheckFu } input := entities.GetEntityInput{ - PartitionKey: partitionKey, - RowKey: rowKey, + PartitionKey: partitionKey, + RowKey: rowKey, + MetaDataLevel: entities.NoMetaData, } resp, err := client.Get(ctx, accountName, tableName, input) if err != nil { @@ -155,7 +156,7 @@ func testCheckAzureRMTableEntityDestroy(s *terraform.State) error { continue } - tableName := rs.Primary.Attributes["share_name"] + tableName := rs.Primary.Attributes["table_name"] accountName := rs.Primary.Attributes["storage_account_name"] partitionKey := rs.Primary.Attributes["parititon_key"] rowKey := rs.Primary.Attributes["row_key"] @@ -206,6 +207,9 @@ resource "azurerm_storage_table_entity" "test" { partition_key = "test_partition%d" row_key = "test_row%d" + entity = { + foo = "bar" + } } `, template, rInt, rInt) } @@ -221,6 +225,9 @@ resource "azurerm_storage_table_entity" "test" { partition_key = "test_partition%d" row_key = "test_row%d" + entity = { + foo = "bar" + } } `, template, rInt, rInt) } @@ -236,6 +243,10 @@ resource "azurerm_storage_table_entity" "test" { partition_key = "test_partition%d" row_key = "test_row%d" + entity = { + foo = "bar" + test = "updated" + } } `, template, rInt, rInt) } diff --git a/website/azurerm.erb b/website/azurerm.erb index 486f3d5044b8..7b86fa1609a4 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -1602,6 +1602,10 @@ azurerm_storage_table +
  • + azurerm_storage_table_entity +
  • + diff --git a/website/docs/r/storage_table.html.markdown b/website/docs/r/storage_table.html.markdown index 506650f0cfc1..ffd7c586a2b4 100644 --- a/website/docs/r/storage_table.html.markdown +++ b/website/docs/r/storage_table.html.markdown @@ -1,7 +1,7 @@ --- layout: "azurerm" page_title: "Azure Resource Manager: azurerm_storage_table" -sidebar_current: "docs-azurerm-resource-storage-table" +sidebar_current: "docs-azurerm-resource-storage-table-x" description: |- Manages a Azure Storage Table. --- diff --git a/website/docs/r/storage_table_entity.html.markdown b/website/docs/r/storage_table_entity.html.markdown new file mode 100644 index 000000000000..9ef8cf351f0d --- /dev/null +++ b/website/docs/r/storage_table_entity.html.markdown @@ -0,0 +1,79 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_storage_table_entity" +sidebar_current: "docs-azurerm-resource-storage-table-entity" +description: |- + Manage an Azure Storage Table Entity. +--- + +# azurerm_storage_table_entity + +Manage an Azure Storage Table Entity. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "test" { + name = "azuretest" + location = "westus" +} + +resource "azurerm_storage_account" "test" { + name = "azureteststorage1" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "westus" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_storage_table" "test" { + name = "mysampletable" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" +} + +resource "azurerm_storage_table_entity" "test" { + storage_account_name = "${azurerm_storage_account.test.name}" + table_name = "${azurerm_storage_table.test.name}" + + partition_key = "samplepartition" + row_key = "samplerow" + + entity = { + sample = "entity" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `storage_account_name` - (Required) Specifies the storage account in which to create the storage table entity. + Changing this forces a new resource to be created. + +* `table_name` - (Required) The name of the storage table in which to create the storage table entity. +Changing this forces a new resource to be created. + +* `partition_key` - (Required) The key for the partition where the entity will be inserted/merged. Changing this forces a new resource. + +* `row_key` - (Required) The key for the row where the entity will be inserted/merged. Changing this forces a new resource. + +* `entity` - (Required) A map of key/value pairs that describe the entity to be inserted/merged in to the storage table. + + +## Attributes Reference + +The following attributes are exported in addition to the arguments listed above: + +* `id` - The ID of the Storage Table Entity. + +## Import + +Storage Table Entities can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_storage_table_entity.entity1 https://example.table.core.windows.net/table1(PartitionKey='samplepartition',RowKey='samplerow') +``` + + From b72a992773506a6adfc0d7ca6575696c0794ed23 Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Thu, 11 Jul 2019 16:38:13 -0700 Subject: [PATCH 3/7] Update website/docs/r/storage_table_entity.html.markdown Co-Authored-By: Tom Harvey --- website/docs/r/storage_table_entity.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/storage_table_entity.html.markdown b/website/docs/r/storage_table_entity.html.markdown index 9ef8cf351f0d..ba7fdc8659b7 100644 --- a/website/docs/r/storage_table_entity.html.markdown +++ b/website/docs/r/storage_table_entity.html.markdown @@ -3,7 +3,7 @@ layout: "azurerm" page_title: "Azure Resource Manager: azurerm_storage_table_entity" sidebar_current: "docs-azurerm-resource-storage-table-entity" description: |- - Manage an Azure Storage Table Entity. + Manages an Entity within a Table in an Azure Storage Account. --- # azurerm_storage_table_entity From d86050aca6f04c80ace456c6b547a90e5407b63d Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Thu, 11 Jul 2019 16:38:20 -0700 Subject: [PATCH 4/7] Update website/docs/r/storage_table_entity.html.markdown Co-Authored-By: Tom Harvey --- website/docs/r/storage_table_entity.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/storage_table_entity.html.markdown b/website/docs/r/storage_table_entity.html.markdown index ba7fdc8659b7..d2f4a2cbfa63 100644 --- a/website/docs/r/storage_table_entity.html.markdown +++ b/website/docs/r/storage_table_entity.html.markdown @@ -8,7 +8,7 @@ description: |- # azurerm_storage_table_entity -Manage an Azure Storage Table Entity. +Manages an Entity within a Table in an Azure Storage Account. ## Example Usage From 311c131a31cd4413d2de4e3cbce398152a9685f3 Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Thu, 11 Jul 2019 16:42:44 -0700 Subject: [PATCH 5/7] Update website/docs/r/storage_table_entity.html.markdown Co-Authored-By: Tom Harvey --- website/docs/r/storage_table_entity.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/storage_table_entity.html.markdown b/website/docs/r/storage_table_entity.html.markdown index d2f4a2cbfa63..c636aff1043e 100644 --- a/website/docs/r/storage_table_entity.html.markdown +++ b/website/docs/r/storage_table_entity.html.markdown @@ -66,7 +66,7 @@ Changing this forces a new resource to be created. The following attributes are exported in addition to the arguments listed above: -* `id` - The ID of the Storage Table Entity. +* `id` - The ID of the Entity within the Table in the Storage Account. ## Import From 6972edf82643d516f1cf9d6c19a04581c6789155 Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Thu, 11 Jul 2019 16:43:14 -0700 Subject: [PATCH 6/7] Update website/docs/r/storage_table_entity.html.markdown Co-Authored-By: Tom Harvey --- website/docs/r/storage_table_entity.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/storage_table_entity.html.markdown b/website/docs/r/storage_table_entity.html.markdown index c636aff1043e..e3fba6773f9a 100644 --- a/website/docs/r/storage_table_entity.html.markdown +++ b/website/docs/r/storage_table_entity.html.markdown @@ -70,7 +70,7 @@ The following attributes are exported in addition to the arguments listed above: ## Import -Storage Table Entities can be imported using the `resource id`, e.g. +Entities within a Table in an Azure Storage Account can be imported using the `resource id`, e.g. ```shell terraform import azurerm_storage_table_entity.entity1 https://example.table.core.windows.net/table1(PartitionKey='samplepartition',RowKey='samplerow') From c084dad1a43e9b3d93d27afec7a5c03728a773ab Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Thu, 11 Jul 2019 16:56:12 -0700 Subject: [PATCH 7/7] Addressing comments --- azurerm/resource_arm_storage_table_entity.go | 37 +++++++++++-------- .../resource_arm_storage_table_entity_test.go | 8 ++-- .../docs/r/storage_table_entity.html.markdown | 36 +++++++++--------- 3 files changed, 44 insertions(+), 37 deletions(-) diff --git a/azurerm/resource_arm_storage_table_entity.go b/azurerm/resource_arm_storage_table_entity.go index cfb793371c0d..e50db3db0120 100644 --- a/azurerm/resource_arm_storage_table_entity.go +++ b/azurerm/resource_arm_storage_table_entity.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "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" "github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities" ) @@ -22,24 +23,28 @@ func resourceArmStorageTableEntity() *schema.Resource { Schema: map[string]*schema.Schema{ "table_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArmStorageTableName, }, "storage_account_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, }, "partition_key": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, }, "row_key": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, }, "entity": { Type: schema.TypeMap, @@ -74,8 +79,8 @@ func resourceArmStorageTableEntityCreateUpdate(d *schema.ResourceData, meta inte if requireResourcesToBeImported { input := entities.GetEntityInput{ - PartitionKey: partitionKey, - RowKey: rowKey, + PartitionKey: partitionKey, + RowKey: rowKey, MetaDataLevel: entities.NoMetaData, } existing, err := client.Get(ctx, accountName, tableName, input) @@ -146,7 +151,9 @@ func resourceArmStorageTableEntityRead(d *schema.ResourceData, meta interface{}) d.Set("table_name", id.TableName) d.Set("partition_key", id.PartitionKey) d.Set("row_key", id.RowKey) - d.Set("entity", flattenEntity(result.Entity)) + if err := d.Set("entity", flattenEntity(result.Entity)); err != nil { + return fmt.Errorf("Error setting `entity` for Entity (Partition Key %q / Row Key %q) (Table %q / Storage Account %q / Resource Group %q): %s", id.PartitionKey, id.RowKey, id.TableName, id.AccountName, *resourceGroup, err) + } return nil } diff --git a/azurerm/resource_arm_storage_table_entity_test.go b/azurerm/resource_arm_storage_table_entity_test.go index 35c68685cab5..36e10a76c47d 100644 --- a/azurerm/resource_arm_storage_table_entity_test.go +++ b/azurerm/resource_arm_storage_table_entity_test.go @@ -208,7 +208,7 @@ resource "azurerm_storage_table_entity" "test" { partition_key = "test_partition%d" row_key = "test_row%d" entity = { - foo = "bar" + Foo = "Bar" } } `, template, rInt, rInt) @@ -226,7 +226,7 @@ resource "azurerm_storage_table_entity" "test" { partition_key = "test_partition%d" row_key = "test_row%d" entity = { - foo = "bar" + Foo = "Bar" } } `, template, rInt, rInt) @@ -244,8 +244,8 @@ resource "azurerm_storage_table_entity" "test" { partition_key = "test_partition%d" row_key = "test_row%d" entity = { - foo = "bar" - test = "updated" + Foo = "Bar" + Test = "Updated" } } `, template, rInt, rInt) diff --git a/website/docs/r/storage_table_entity.html.markdown b/website/docs/r/storage_table_entity.html.markdown index 9ef8cf351f0d..8b590ed815dc 100644 --- a/website/docs/r/storage_table_entity.html.markdown +++ b/website/docs/r/storage_table_entity.html.markdown @@ -3,44 +3,44 @@ layout: "azurerm" page_title: "Azure Resource Manager: azurerm_storage_table_entity" sidebar_current: "docs-azurerm-resource-storage-table-entity" description: |- - Manage an Azure Storage Table Entity. + Manages a Azure Storage Table Entity. --- # azurerm_storage_table_entity -Manage an Azure Storage Table Entity. +Manages a Azure Storage Table Entity. ## Example Usage ```hcl -resource "azurerm_resource_group" "test" { - name = "azuretest" +resource "azurerm_resource_group" "example" { + name = "azureexample" location = "westus" } -resource "azurerm_storage_account" "test" { - name = "azureteststorage1" - resource_group_name = "${azurerm_resource_group.test.name}" - location = "westus" +resource "azurerm_storage_account" "example" { + name = "azureexamplestorage1" + resource_group_name = "${azurerm_resource_group.example.name}" + location = "${azurerm_resource_group.example.location}" account_tier = "Standard" account_replication_type = "LRS" } -resource "azurerm_storage_table" "test" { - name = "mysampletable" - resource_group_name = "${azurerm_resource_group.test.name}" - storage_account_name = "${azurerm_storage_account.test.name}" +resource "azurerm_storage_table" "example" { + name = "myexampletable" + resource_group_name = "${azurerm_resource_group.example.name}" + storage_account_name = "${azurerm_storage_account.example.name}" } -resource "azurerm_storage_table_entity" "test" { - storage_account_name = "${azurerm_storage_account.test.name}" - table_name = "${azurerm_storage_table.test.name}" +resource "azurerm_storage_table_entity" "example" { + storage_account_name = "${azurerm_storage_account.example.name}" + table_name = "${azurerm_storage_table.example.name}" - partition_key = "samplepartition" - row_key = "samplerow" + partition_key = "examplepartition" + row_key = "exmamplerow" entity = { - sample = "entity" + example = "sample" } } ```