Skip to content

Commit af6abe4

Browse files
r/storage_blob: support for inline content
fixes #3876
1 parent 01e17d5 commit af6abe4

File tree

4 files changed

+159
-20
lines changed

4 files changed

+159
-20
lines changed

azurerm/internal/services/storage/blobs.go

+49-7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"context"
66
"fmt"
77
"io"
8+
"io/ioutil"
89
"os"
910
"runtime"
1011
"strings"
@@ -24,27 +25,33 @@ type BlobUpload struct {
2425
BlobName string
2526
ContainerName string
2627

27-
BlobType string
28-
ContentType string
29-
MetaData map[string]string
30-
Parallelism int
31-
Size int
32-
Source string
33-
SourceUri string
28+
BlobType string
29+
ContentType string
30+
MetaData map[string]string
31+
Parallelism int
32+
Size int
33+
Source string
34+
SourceContent string
35+
SourceUri string
3436
}
3537

3638
func (sbu BlobUpload) Create(ctx context.Context) error {
39+
// TODO: should we move this into the Block and Page blocks?
3740
if sbu.SourceUri != "" {
3841
return sbu.copy(ctx)
3942
}
4043

4144
blobType := strings.ToLower(sbu.BlobType)
4245

4346
if blobType == "append" {
47+
// TODO: if Source/SourceContent are set return an error
4448
return sbu.createEmptyAppendBlob(ctx)
4549
}
4650

4751
if blobType == "block" {
52+
if sbu.SourceContent != "" {
53+
return sbu.uploadBlockBlobFromContent(ctx)
54+
}
4855
if sbu.Source != "" {
4956
return sbu.uploadBlockBlob(ctx)
5057
}
@@ -53,6 +60,9 @@ func (sbu BlobUpload) Create(ctx context.Context) error {
5360
}
5461

5562
if blobType == "page" {
63+
if sbu.SourceContent != "" {
64+
return sbu.uploadPageBlobFromContent(ctx)
65+
}
5666
if sbu.Source != "" {
5767
return sbu.uploadPageBlob(ctx)
5868
}
@@ -99,6 +109,22 @@ func (sbu BlobUpload) createEmptyBlockBlob(ctx context.Context) error {
99109
return nil
100110
}
101111

112+
func (sbu BlobUpload) uploadBlockBlobFromContent(ctx context.Context) error {
113+
tmpFile, err := ioutil.TempFile(os.TempDir(), "upload-")
114+
if err != nil {
115+
return fmt.Errorf("Error creating temporary file: %s", err)
116+
}
117+
defer os.Remove(tmpFile.Name())
118+
119+
if _, err = tmpFile.Write([]byte(sbu.SourceContent)); err != nil {
120+
return fmt.Errorf("Error writing Source Content to Temp File: %s", err)
121+
}
122+
defer tmpFile.Close()
123+
124+
sbu.Source = tmpFile.Name()
125+
return sbu.uploadBlockBlob(ctx)
126+
}
127+
102128
func (sbu BlobUpload) uploadBlockBlob(ctx context.Context) error {
103129
file, err := os.Open(sbu.Source)
104130
if err != nil {
@@ -134,6 +160,22 @@ func (sbu BlobUpload) createEmptyPageBlob(ctx context.Context) error {
134160
return nil
135161
}
136162

163+
func (sbu BlobUpload) uploadPageBlobFromContent(ctx context.Context) error {
164+
tmpFile, err := ioutil.TempFile(os.TempDir(), "upload-")
165+
if err != nil {
166+
return fmt.Errorf("Error creating temporary file: %s", err)
167+
}
168+
defer os.Remove(tmpFile.Name())
169+
170+
if _, err = tmpFile.Write([]byte(sbu.SourceContent)); err != nil {
171+
return fmt.Errorf("Error writing Source Content to Temp File: %s", err)
172+
}
173+
defer tmpFile.Close()
174+
175+
sbu.Source = tmpFile.Name()
176+
return sbu.uploadPageBlob(ctx)
177+
}
178+
137179
func (sbu BlobUpload) uploadPageBlob(ctx context.Context) error {
138180
if sbu.Size != 0 {
139181
return fmt.Errorf("`size` cannot be set for an uploaded page blob")

azurerm/resource_arm_storage_blob.go

+19-10
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func resourceArmStorageBlob() *schema.Resource {
7474
"access_tier": {
7575
Type: schema.TypeString,
7676
Optional: true,
77-
Default: string(blobs.Hot),
77+
Computed: true,
7878
ValidateFunc: validation.StringInSlice([]string{
7979
string(blobs.Archive),
8080
string(blobs.Cool),
@@ -92,14 +92,21 @@ func resourceArmStorageBlob() *schema.Resource {
9292
Type: schema.TypeString,
9393
Optional: true,
9494
ForceNew: true,
95-
ConflictsWith: []string{"source_uri"},
95+
ConflictsWith: []string{"source_uri", "source_content"},
96+
},
97+
98+
"source_content": {
99+
Type: schema.TypeString,
100+
Optional: true,
101+
ForceNew: true,
102+
ConflictsWith: []string{"source", "source_uri"},
96103
},
97104

98105
"source_uri": {
99106
Type: schema.TypeString,
100107
Optional: true,
101108
ForceNew: true,
102-
ConflictsWith: []string{"source"},
109+
ConflictsWith: []string{"source", "source_content"},
103110
},
104111

105112
"url": {
@@ -176,13 +183,14 @@ func resourceArmStorageBlobCreate(d *schema.ResourceData, meta interface{}) erro
176183
BlobName: name,
177184
Client: blobsClient,
178185

179-
BlobType: d.Get("type").(string),
180-
ContentType: d.Get("content_type").(string),
181-
MetaData: storage.ExpandMetaData(metaDataRaw),
182-
Parallelism: d.Get("parallelism").(int),
183-
Size: d.Get("size").(int),
184-
Source: d.Get("source").(string),
185-
SourceUri: d.Get("source_uri").(string),
186+
BlobType: d.Get("type").(string),
187+
ContentType: d.Get("content_type").(string),
188+
MetaData: storage.ExpandMetaData(metaDataRaw),
189+
Parallelism: d.Get("parallelism").(int),
190+
Size: d.Get("size").(int),
191+
Source: d.Get("source").(string),
192+
SourceContent: d.Get("source_content").(string),
193+
SourceUri: d.Get("source_uri").(string),
186194
}
187195
if err := blobInput.Create(ctx); err != nil {
188196
return fmt.Errorf("Error creating Blob %q (Container %q / Account %q): %s", name, containerName, accountName, err)
@@ -217,6 +225,7 @@ func resourceArmStorageBlobUpdate(d *schema.ResourceData, meta interface{}) erro
217225
}
218226

219227
if d.HasChange("access_tier") {
228+
// this is only applicable for Gen2/BlobStorage accounts
220229
log.Printf("[DEBUG] Updating Access Tier for Blob %q (Container %q / Account %q)...", id.BlobName, id.ContainerName, id.AccountName)
221230
accessTier := blobs.AccessTier(d.Get("access_tier").(string))
222231

azurerm/resource_arm_storage_blob_test.go

+86
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,33 @@ func TestAccAzureRMStorageBlob_blockEmptyAccessTier(t *testing.T) {
190190
})
191191
}
192192

193+
func TestAccAzureRMStorageBlob_blockFromInlineContent(t *testing.T) {
194+
resourceName := "azurerm_storage_blob.test"
195+
ri := tf.AccRandTimeInt()
196+
rs := strings.ToLower(acctest.RandString(11))
197+
location := testLocation()
198+
199+
resource.ParallelTest(t, resource.TestCase{
200+
PreCheck: func() { testAccPreCheck(t) },
201+
Providers: testAccProviders,
202+
CheckDestroy: testCheckAzureRMStorageBlobDestroy,
203+
Steps: []resource.TestStep{
204+
{
205+
Config: testAccAzureRMStorageBlob_blockFromInlineContent(ri, rs, location),
206+
Check: resource.ComposeTestCheckFunc(
207+
testCheckAzureRMStorageBlobExists(resourceName),
208+
),
209+
},
210+
{
211+
ResourceName: resourceName,
212+
ImportState: true,
213+
ImportStateVerify: true,
214+
ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "source_content", "type"},
215+
},
216+
},
217+
})
218+
}
219+
193220
func TestAccAzureRMStorageBlob_blockFromPublicBlob(t *testing.T) {
194221
resourceName := "azurerm_storage_blob.test"
195222
ri := tf.AccRandTimeInt()
@@ -428,6 +455,33 @@ func TestAccAzureRMStorageBlob_pageFromExistingBlob(t *testing.T) {
428455
})
429456
}
430457

458+
func TestAccAzureRMStorageBlob_pageFromInlineContent(t *testing.T) {
459+
resourceName := "azurerm_storage_blob.test"
460+
ri := tf.AccRandTimeInt()
461+
rs := strings.ToLower(acctest.RandString(11))
462+
location := testLocation()
463+
464+
resource.ParallelTest(t, resource.TestCase{
465+
PreCheck: func() { testAccPreCheck(t) },
466+
Providers: testAccProviders,
467+
CheckDestroy: testCheckAzureRMStorageBlobDestroy,
468+
Steps: []resource.TestStep{
469+
{
470+
Config: testAccAzureRMStorageBlob_pageFromInlineContent(ri, rs, location),
471+
Check: resource.ComposeTestCheckFunc(
472+
testCheckAzureRMStorageBlobExists(resourceName),
473+
),
474+
},
475+
{
476+
ResourceName: resourceName,
477+
ImportState: true,
478+
ImportStateVerify: true,
479+
ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "source_content", "type"},
480+
},
481+
},
482+
})
483+
}
484+
431485
func TestAccAzureRMStorageBlob_pageFromLocalFile(t *testing.T) {
432486
sourceBlob, err := ioutil.TempFile("", "")
433487
if err != nil {
@@ -788,6 +842,22 @@ resource "azurerm_storage_blob" "test" {
788842
`, template, string(accessTier))
789843
}
790844

845+
func testAccAzureRMStorageBlob_blockFromInlineContent(rInt int, rString, location string) string {
846+
template := testAccAzureRMStorageBlob_template(rInt, rString, location, "blob")
847+
return fmt.Sprintf(`
848+
%s
849+
850+
resource "azurerm_storage_blob" "test" {
851+
name = "rick.morty"
852+
resource_group_name = "${azurerm_resource_group.test.name}"
853+
storage_account_name = "${azurerm_storage_account.test.name}"
854+
storage_container_name = "${azurerm_storage_container.test.name}"
855+
type = "block"
856+
source_content = "Wubba Lubba Dub Dub"
857+
}
858+
`, template)
859+
}
860+
791861
func testAccAzureRMStorageBlob_blockFromPublicBlob(rInt int, rString, location string) string {
792862
template := testAccAzureRMStorageBlob_template(rInt, rString, location, "blob")
793863
return fmt.Sprintf(`
@@ -979,6 +1049,22 @@ resource "azurerm_storage_blob" "test" {
9791049
`, template)
9801050
}
9811051

1052+
func testAccAzureRMStorageBlob_pageFromInlineContent(rInt int, rString, location string) string {
1053+
template := testAccAzureRMStorageBlob_template(rInt, rString, location, "private")
1054+
return fmt.Sprintf(`
1055+
%s
1056+
1057+
resource "azurerm_storage_blob" "test" {
1058+
name = "rick.morty"
1059+
resource_group_name = "${azurerm_resource_group.test.name}"
1060+
storage_account_name = "${azurerm_storage_account.test.name}"
1061+
storage_container_name = "${azurerm_storage_container.test.name}"
1062+
type = "page"
1063+
source_content = "Wubba Lubba Dub Dub"
1064+
}
1065+
`, template)
1066+
}
1067+
9821068
func testAccAzureRMStorageBlob_pageFromLocalBlob(rInt int, rString, location, fileName string) string {
9831069
template := testAccAzureRMStorageBlob_template(rInt, rString, location, "private")
9841070
return fmt.Sprintf(`

website/docs/r/storage_blob.html.markdown

+5-3
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,16 @@ The following arguments are supported:
5858

5959
* `size` - (Optional) Used only for `page` blobs to specify the size in bytes of the blob to be created. Must be a multiple of 512. Defaults to 0.
6060

61-
* `access_tier` - (Optional) The access tier of the storage blob. Possible values are `Archive`, `Cool` and `Hot`. Defaults to `Hot`.
61+
* `access_tier` - (Optional) The access tier of the storage blob. Possible values are `Archive`, `Cool` and `Hot`.
6262

6363
* `content_type` - (Optional) The content type of the storage blob. Cannot be defined if `source_uri` is defined. Defaults to `application/octet-stream`.
6464

65-
* `source` - (Optional) An absolute path to a file on the local system. Cannot be defined if `source_uri` is defined.
65+
* `source` - (Optional) An absolute path to a file on the local system. Cannot be specified if `source_content` or `source_uri` is specified.
66+
67+
* `source_content` - (Optional) The content for this blob which should be defined inline. Cannot be specified if `source` or `source_uri` is specified.
6668

6769
* `source_uri` - (Optional) The URI of an existing blob, or a file in the Azure File service, to use as the source contents
68-
for the blob to be created. Changing this forces a new resource to be created. Cannot be defined if `source` is defined.
70+
for the blob to be created. Changing this forces a new resource to be created. Cannot be specified if `source` or `source_content` is specified.
6971

7072
* `parallelism` - (Optional) The number of workers per CPU core to run for concurrent uploads. Defaults to `8`.
7173

0 commit comments

Comments
 (0)