-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New datasource
azurerm_storage_table_entities
(#24973)
* New datasource `azurerm_storage_table_entities` Allows retrieving multiple storage table entities based on a filter query. ``` ✗ TF_ACC=1 go test -v ./internal/services/storage -timeout=1000m -run='TestAccDataSourceStorageTableEntities_basic' === RUN TestAccDataSourceStorageTableEntities_basic === PAUSE TestAccDataSourceStorageTableEntities_basic === CONT TestAccDataSourceStorageTableEntities_basic --- PASS: TestAccDataSourceStorageTableEntities_basic (146.35s) PASS ok github.com/hashicorp/terraform-provider-azurerm/internal/services/storage 149.796s ``` * Fix schema semantics * make generate * Fix express_route_circuit_peering datasource * terrafmt
- Loading branch information
Showing
7 changed files
with
417 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package parse | ||
|
||
import ( | ||
"crypto/sha1" | ||
"encoding/hex" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" | ||
"github.com/hashicorp/terraform-provider-azurerm/utils" | ||
) | ||
|
||
// TODO: tests for this | ||
var _ resourceids.Id = StorageTableEntitiesId{} | ||
|
||
type StorageTableEntitiesId struct { | ||
AccountName string | ||
DomainSuffix string | ||
TableName string | ||
Filter string | ||
} | ||
|
||
func (id StorageTableEntitiesId) String() string { | ||
components := []string{ | ||
fmt.Sprintf("Account Name %q", id.AccountName), | ||
fmt.Sprintf("Domain Suffix %q", id.DomainSuffix), | ||
fmt.Sprintf("TableName %q", id.TableName), | ||
fmt.Sprintf("Filter %q", id.Filter), | ||
} | ||
return fmt.Sprintf("Storage Table %s", strings.Join(components, " / ")) | ||
} | ||
|
||
func (id StorageTableEntitiesId) ID() string { | ||
return fmt.Sprintf("https://%s.table.%s/%s(%s)", id.AccountName, id.DomainSuffix, id.TableName, id.Filter) | ||
} | ||
|
||
func NewStorageTableEntitiesId(accountName, domainSuffix, tablename, filter string) StorageTableEntitiesId { | ||
s := utils.Base64EncodeIfNot(filter) | ||
sha := sha1.Sum([]byte(s)) | ||
filterHash := hex.EncodeToString(sha[:]) | ||
return StorageTableEntitiesId{ | ||
AccountName: accountName, | ||
DomainSuffix: domainSuffix, | ||
TableName: tablename, | ||
Filter: filterHash, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
213 changes: 213 additions & 0 deletions
213
internal/services/storage/storage_table_entities_data_source.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package storage | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"strings" | ||
"time" | ||
|
||
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk" | ||
"github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/parse" | ||
"github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate" | ||
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" | ||
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" | ||
"github.com/tombuildsstuff/giovanni/storage/2020-08-04/table/entities" | ||
) | ||
|
||
type storageTableEntitiesDataSource struct{} | ||
|
||
var _ sdk.DataSource = storageTableEntitiesDataSource{} | ||
|
||
type TableEntitiesDataSourceModel struct { | ||
TableName string `tfschema:"table_name"` | ||
StorageAccountName string `tfschema:"storage_account_name"` | ||
Filter string `tfschema:"filter"` | ||
Items []TableEntitiyDataSourceModel `tfschema:"items"` | ||
} | ||
|
||
type TableEntitiyDataSourceModel struct { | ||
PartitionKey string `tfschema:"partition_key"` | ||
RowKey string `tfschema:"row_key"` | ||
Properties map[string]interface{} `tfschema:"properties"` | ||
} | ||
|
||
func (k storageTableEntitiesDataSource) Arguments() map[string]*pluginsdk.Schema { | ||
return map[string]*pluginsdk.Schema{ | ||
"table_name": { | ||
Type: pluginsdk.TypeString, | ||
Required: true, | ||
ValidateFunc: validate.StorageTableName, | ||
}, | ||
|
||
"storage_account_name": { | ||
Type: pluginsdk.TypeString, | ||
Required: true, | ||
ValidateFunc: validate.StorageAccountName, | ||
}, | ||
|
||
"filter": { | ||
Type: pluginsdk.TypeString, | ||
Required: true, | ||
ValidateFunc: validation.StringIsNotEmpty, | ||
}, | ||
} | ||
} | ||
|
||
func (k storageTableEntitiesDataSource) Attributes() map[string]*pluginsdk.Schema { | ||
return map[string]*pluginsdk.Schema{ | ||
"items": { | ||
Type: pluginsdk.TypeList, | ||
Computed: true, | ||
Elem: &pluginsdk.Resource{ | ||
Schema: map[string]*pluginsdk.Schema{ | ||
"partition_key": { | ||
Type: pluginsdk.TypeString, | ||
Computed: true, | ||
}, | ||
|
||
"row_key": { | ||
Type: pluginsdk.TypeString, | ||
Computed: true, | ||
}, | ||
|
||
"properties": { | ||
Type: pluginsdk.TypeMap, | ||
Computed: true, | ||
Elem: &pluginsdk.Schema{ | ||
Type: pluginsdk.TypeString, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func (k storageTableEntitiesDataSource) ModelObject() interface{} { | ||
return &TableEntitiesDataSourceModel{} | ||
} | ||
|
||
func (k storageTableEntitiesDataSource) ResourceType() string { | ||
return "azurerm_storage_table_entities" | ||
} | ||
|
||
func (k storageTableEntitiesDataSource) Read() sdk.ResourceFunc { | ||
return sdk.ResourceFunc{ | ||
Timeout: 5 * time.Minute, | ||
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { | ||
var model TableEntitiesDataSourceModel | ||
if err := metadata.Decode(&model); err != nil { | ||
return err | ||
} | ||
|
||
storageClient := metadata.Client.Storage | ||
|
||
account, err := storageClient.FindAccount(ctx, model.StorageAccountName) | ||
if err != nil { | ||
return fmt.Errorf("retrieving Account %q for Table %q: %s", model.StorageAccountName, model.TableName, err) | ||
} | ||
if account == nil { | ||
return fmt.Errorf("the parent Storage Account %s was not found", model.StorageAccountName) | ||
} | ||
|
||
client, err := storageClient.TableEntityClient(ctx, *account) | ||
if err != nil { | ||
return fmt.Errorf("building Table Entity Client for Storage Account %q (Resource Group %q): %s", model.StorageAccountName, account.ResourceGroup, err) | ||
} | ||
|
||
input := entities.QueryEntitiesInput{ | ||
Filter: &model.Filter, | ||
MetaDataLevel: entities.MinimalMetaData, | ||
} | ||
|
||
id := parse.NewStorageTableEntitiesId(model.StorageAccountName, storageClient.Environment.StorageEndpointSuffix, model.TableName, model.Filter) | ||
|
||
result, err := client.Query(ctx, model.StorageAccountName, model.TableName, input) | ||
if err != nil { | ||
return fmt.Errorf("retrieving Entities (Filter %q) (Table %q / Storage Account %q / Resource Group %q): %s", model.Filter, model.TableName, model.StorageAccountName, account.ResourceGroup, err) | ||
} | ||
|
||
var flattenedEntities []TableEntitiyDataSourceModel | ||
for _, entity := range result.Entities { | ||
flattenedEntity := flattenEntityWithMetadata(entity) | ||
flattenedEntities = append(flattenedEntities, flattenedEntity) | ||
} | ||
model.Items = flattenedEntities | ||
metadata.SetID(id) | ||
|
||
return metadata.Encode(&model) | ||
}, | ||
} | ||
} | ||
|
||
// The api returns extra information that we already have. We'll remove it here before setting it in state. | ||
func flattenEntityWithMetadata(entity map[string]interface{}) TableEntitiyDataSourceModel { | ||
delete(entity, "Timestamp") | ||
|
||
result := TableEntitiyDataSourceModel{} | ||
|
||
for k, v := range entity { | ||
properties := map[string]interface{}{} | ||
if k == "PartitionKey" { | ||
result.PartitionKey = v.(string) | ||
continue | ||
} | ||
|
||
if k == "RowKey" { | ||
result.RowKey = v.(string) | ||
continue | ||
} | ||
// skip ODATA annotation returned with fullmetadata | ||
if strings.HasPrefix(k, "odata.") || strings.HasSuffix(k, "@odata.type") { | ||
continue | ||
} | ||
if dtype, ok := entity[k+"@odata.type"]; ok { | ||
switch dtype { | ||
case "Edm.Boolean": | ||
properties[k] = fmt.Sprint(v) | ||
case "Edm.Double": | ||
properties[k] = fmt.Sprintf("%f", v) | ||
case "Edm.Int32", "Edm.Int64": | ||
// `v` returned as string for int 64 | ||
properties[k] = fmt.Sprint(v) | ||
case "Edm.String": | ||
properties[k] = v | ||
default: | ||
log.Printf("[WARN] key %q with unexpected @odata.type %q", k, dtype) | ||
continue | ||
} | ||
|
||
properties[k+"@odata.type"] = dtype | ||
result.Properties = properties | ||
} else { | ||
// special handling for property types that do not require the annotation to be present | ||
// https://docs.microsoft.com/en-us/rest/api/storageservices/payload-format-for-table-service-operations#property-types-in-a-json-feed | ||
switch c := v.(type) { | ||
case bool: | ||
properties[k] = fmt.Sprint(v) | ||
properties[k+"@odata.type"] = "Edm.Boolean" | ||
case float64: | ||
f64 := v.(float64) | ||
if v == float64(int64(f64)) { | ||
properties[k] = fmt.Sprintf("%d", int64(f64)) | ||
properties[k+"@odata.type"] = "Edm.Int32" | ||
} else { | ||
// fmt.Sprintf("%f", v) will return `123.123000` for `123.123`, have to use fmt.Sprint | ||
properties[k] = fmt.Sprint(v) | ||
properties[k+"@odata.type"] = "Edm.Double" | ||
} | ||
case string: | ||
properties[k] = v | ||
default: | ||
log.Printf("[WARN] key %q with unexpected type %T", k, c) | ||
} | ||
result.Properties = properties | ||
} | ||
} | ||
|
||
return result | ||
} |
98 changes: 98 additions & 0 deletions
98
internal/services/storage/storage_table_entities_data_source_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package storage_test | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" | ||
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" | ||
) | ||
|
||
type StorageTableEntitiesDataSource struct{} | ||
|
||
func TestAccDataSourceStorageTableEntities_basic(t *testing.T) { | ||
data := acceptance.BuildTestData(t, "data.azurerm_storage_table_entities", "test") | ||
|
||
data.DataSourceTest(t, []acceptance.TestStep{ | ||
{ | ||
Config: StorageTableEntitiesDataSource{}.basicWithDataSource(data), | ||
Check: acceptance.ComposeTestCheckFunc( | ||
check.That(data.ResourceName).Key("items.#").HasValue("2"), | ||
), | ||
}, | ||
}) | ||
} | ||
|
||
func (d StorageTableEntitiesDataSource) basic(data acceptance.TestData) string { | ||
return fmt.Sprintf(` | ||
provider "azurerm" { | ||
features {} | ||
} | ||
resource "azurerm_resource_group" "test" { | ||
name = "tableentitydstest-%s" | ||
location = "%s" | ||
} | ||
resource "azurerm_storage_account" "test" { | ||
name = "acctesttedsc%s" | ||
resource_group_name = "${azurerm_resource_group.test.name}" | ||
location = "${azurerm_resource_group.test.location}" | ||
account_tier = "Standard" | ||
account_replication_type = "LRS" | ||
allow_nested_items_to_be_public = false | ||
} | ||
resource "azurerm_storage_table" "test" { | ||
name = "tabletesttedsc%s" | ||
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 = "testpartition" | ||
row_key = "testrow" | ||
entity = { | ||
testkey = "testval" | ||
} | ||
} | ||
resource "azurerm_storage_table_entity" "test2" { | ||
storage_account_name = azurerm_storage_account.test.name | ||
table_name = azurerm_storage_table.test.name | ||
partition_key = "testpartition" | ||
row_key = "testrow2" | ||
entity = { | ||
testkey = "testval2" | ||
} | ||
} | ||
`, data.RandomString, data.Locations.Primary, data.RandomString, data.RandomString) | ||
} | ||
|
||
func (d StorageTableEntitiesDataSource) basicWithDataSource(data acceptance.TestData) string { | ||
config := d.basic(data) | ||
return fmt.Sprintf(` | ||
%s | ||
data "azurerm_storage_table_entities" "test" { | ||
table_name = azurerm_storage_table_entity.test.table_name | ||
storage_account_name = azurerm_storage_table_entity.test.storage_account_name | ||
filter = "PartitionKey eq 'testpartition'" | ||
depends_on = [ | ||
azurerm_storage_table_entity.test, | ||
azurerm_storage_table_entity.test2, | ||
] | ||
} | ||
`, config) | ||
} |
Oops, something went wrong.