Skip to content

Commit 9a0f7c7

Browse files
Add support for specifying storage pool with name or partial url. (#12111) (#20502)
[upstream:d507ab44ee85a34fdb41c218ad41e4fb25d84356] Signed-off-by: Modular Magician <[email protected]>
1 parent 1f51b98 commit 9a0f7c7

File tree

7 files changed

+290
-6
lines changed

7 files changed

+290
-6
lines changed

.changelog/12111.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
compute: add support for specifying storage pool with name or partial url
3+
```

google/services/compute/resource_compute_disk.go

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,44 @@ func suppressWindowsFamilyDiff(imageName, familyName string) bool {
293293
return strings.Contains(updatedImageName, updatedFamilyString)
294294
}
295295

296+
// ExpandStoragePoolUrl returns a full self link from a partial self link.
297+
func ExpandStoragePoolUrl(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (string, error) {
298+
// It does not try to construct anything from empty.
299+
if v == nil || v.(string) == "" {
300+
return "", nil
301+
}
302+
303+
project, err := tpgresource.GetProject(d, config)
304+
if err != nil {
305+
return "", err
306+
}
307+
zone, err := tpgresource.GetZone(d, config)
308+
if err != nil {
309+
return "", err
310+
}
311+
312+
formattedStr := v.(string)
313+
if strings.HasPrefix(v.(string), "/") {
314+
formattedStr = formattedStr[1:]
315+
}
316+
replacedStr := ""
317+
318+
if strings.HasPrefix(formattedStr, "https://") {
319+
// Anything that starts with a URL scheme is assumed to be a self link worth using.
320+
return formattedStr, nil
321+
} else if strings.HasPrefix(formattedStr, "projects/") {
322+
// If the self link references a project, we'll just stuck the compute prefix on it
323+
replacedStr = config.ComputeBasePath + formattedStr
324+
} else if strings.HasPrefix(formattedStr, "zones/") {
325+
// For regional or zonal resources which include their region or zone, just put the project in front.
326+
replacedStr = config.ComputeBasePath + "projects/" + project + "/" + formattedStr
327+
} else {
328+
// Anything else is assumed to be a zonal resource, with a partial link that begins with the resource name.
329+
replacedStr = config.ComputeBasePath + "projects/" + project + "/zones/" + zone + "/storagePools/" + formattedStr
330+
}
331+
return replacedStr, nil
332+
}
333+
296334
func ResourceComputeDisk() *schema.Resource {
297335
return &schema.Resource{
298336
Create: resourceComputeDiskCreate,
@@ -649,10 +687,12 @@ encryption key that protects this resource.`,
649687
Optional: true,
650688
ForceNew: true,
651689
DiffSuppressFunc: tpgresource.CompareResourceNames,
652-
Description: `The URL of the storage pool in which the new disk is created.
690+
Description: `The URL or the name of the storage pool in which the new disk is created.
653691
For example:
654692
* https://www.googleapis.com/compute/v1/projects/{project}/zones/{zone}/storagePools/{storagePool}
655-
* /projects/{project}/zones/{zone}/storagePools/{storagePool}`,
693+
* /projects/{project}/zones/{zone}/storagePools/{storagePool}
694+
* /zones/{zone}/storagePools/{storagePool}
695+
* /{storagePool}`,
656696
},
657697
"type": {
658698
Type: schema.TypeString,
@@ -2014,7 +2054,7 @@ func expandComputeDiskLicenses(v interface{}, d tpgresource.TerraformResourceDat
20142054
}
20152055

20162056
func expandComputeDiskStoragePool(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
2017-
return v, nil
2057+
return ExpandStoragePoolUrl(v, d, config)
20182058
}
20192059

20202060
func expandComputeDiskAccessMode(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {

google/services/compute/resource_compute_disk_test.go

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"github.com/hashicorp/terraform-provider-google/google/acctest"
1313
"github.com/hashicorp/terraform-provider-google/google/envvar"
1414
tpgcompute "github.com/hashicorp/terraform-provider-google/google/services/compute"
15+
"github.com/hashicorp/terraform-provider-google/google/tpgresource"
16+
transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport"
1517

1618
"google.golang.org/api/compute/v1"
1719
)
@@ -1437,6 +1439,28 @@ func TestAccComputeDisk_storagePoolSpecified(t *testing.T) {
14371439
})
14381440
}
14391441

1442+
func TestAccComputeDisk_storagePoolSpecified_nameOnly(t *testing.T) {
1443+
t.Parallel()
1444+
1445+
acctest.BootstrapComputeStoragePool(t, "basic-2", "hyperdisk-throughput")
1446+
diskName := fmt.Sprintf("tf-test-disk-%s", acctest.RandString(t, 10))
1447+
1448+
acctest.VcrTest(t, resource.TestCase{
1449+
PreCheck: func() { acctest.AccTestPreCheck(t) },
1450+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
1451+
Steps: []resource.TestStep{
1452+
{
1453+
Config: testAccComputeDisk_storagePoolSpecified(diskName, "tf-bootstrap-storage-pool-hyperdisk-throughput-basic-2"),
1454+
},
1455+
{
1456+
ResourceName: "google_compute_disk.foobar",
1457+
ImportState: true,
1458+
ImportStateVerify: true,
1459+
},
1460+
},
1461+
})
1462+
}
1463+
14401464
func testAccComputeDisk_storagePoolSpecified(diskName, storagePoolUrl string) string {
14411465
return fmt.Sprintf(`
14421466
resource "google_compute_disk" "foobar" {
@@ -1449,6 +1473,189 @@ resource "google_compute_disk" "foobar" {
14491473
`, diskName, storagePoolUrl)
14501474
}
14511475

1476+
func TestExpandStoragePoolUrl_withDataProjectAndZone(t *testing.T) {
1477+
config := &transport_tpg.Config{
1478+
ComputeBasePath: "https://www.googleapis.com/compute/v1/",
1479+
Project: "other-project",
1480+
Zone: "other-zone",
1481+
}
1482+
1483+
data := &tpgresource.ResourceDataMock{
1484+
FieldsInSchema: map[string]interface{}{
1485+
"project": "test-project",
1486+
"zone": "test-zone",
1487+
},
1488+
}
1489+
1490+
name := "test-storage-pool"
1491+
zoneUrl := "zones/test-zone/storagePools/" + name
1492+
projectUrl := "projects/test-project/" + zoneUrl
1493+
fullUrl := config.ComputeBasePath + projectUrl
1494+
1495+
cases := []struct {
1496+
name string
1497+
inputStr string
1498+
}{
1499+
{
1500+
name: "full url",
1501+
inputStr: fullUrl,
1502+
},
1503+
{
1504+
name: "project/{project}/zones/{zone}/storagePools/{storagePool}",
1505+
inputStr: projectUrl,
1506+
},
1507+
{
1508+
name: "/project/{project}/zones/{zone}/storagePools/{storagePool}",
1509+
inputStr: "/" + projectUrl,
1510+
},
1511+
{
1512+
name: "zones/{zone}/storagePools/{storagePool}",
1513+
inputStr: zoneUrl,
1514+
},
1515+
{
1516+
name: "/zones/{zone}/storagePools/{storagePool}",
1517+
inputStr: "/" + zoneUrl,
1518+
},
1519+
{
1520+
name: "{storagePool}",
1521+
inputStr: name,
1522+
},
1523+
{
1524+
name: "/{storagePool}",
1525+
inputStr: "/" + name,
1526+
},
1527+
}
1528+
1529+
for _, tc := range cases {
1530+
tc := tc
1531+
t.Run(tc.name, func(t *testing.T) {
1532+
t.Parallel()
1533+
result, _ := tpgcompute.ExpandStoragePoolUrl(tc.inputStr, data, config)
1534+
if result != fullUrl {
1535+
t.Fatalf("%s does not match with expected full url: %s", result, fullUrl)
1536+
}
1537+
})
1538+
}
1539+
}
1540+
1541+
func TestExpandStoragePoolUrl_withConfigProjectAndZone(t *testing.T) {
1542+
config := &transport_tpg.Config{
1543+
ComputeBasePath: "https://www.googleapis.com/compute/v1/",
1544+
Project: "test-project",
1545+
Zone: "test-zone",
1546+
}
1547+
1548+
data := &tpgresource.ResourceDataMock{}
1549+
1550+
name := "test-storage-pool"
1551+
zoneUrl := "zones/test-zone/storagePools/" + name
1552+
projectUrl := "projects/test-project/" + zoneUrl
1553+
fullUrl := config.ComputeBasePath + projectUrl
1554+
1555+
cases := []struct {
1556+
name string
1557+
inputStr string
1558+
}{
1559+
{
1560+
name: "full url",
1561+
inputStr: fullUrl,
1562+
},
1563+
{
1564+
name: "project/{project}/zones/{zone}/storagePools/{storagePool}",
1565+
inputStr: projectUrl,
1566+
},
1567+
{
1568+
name: "/project/{project}/zones/{zone}/storagePools/{storagePool}",
1569+
inputStr: "/" + projectUrl,
1570+
},
1571+
{
1572+
name: "zones/{zone}/storagePools/{storagePool}",
1573+
inputStr: zoneUrl,
1574+
},
1575+
{
1576+
name: "/zones/{zone}/storagePools/{storagePool}",
1577+
inputStr: "/" + zoneUrl,
1578+
},
1579+
{
1580+
name: "{storagePool}",
1581+
inputStr: name,
1582+
},
1583+
{
1584+
name: "/{storagePool}",
1585+
inputStr: "/" + name,
1586+
},
1587+
}
1588+
1589+
for _, tc := range cases {
1590+
tc := tc
1591+
t.Run(tc.name, func(t *testing.T) {
1592+
t.Parallel()
1593+
result, _ := tpgcompute.ExpandStoragePoolUrl(tc.inputStr, data, config)
1594+
if result != fullUrl {
1595+
t.Fatalf("%s does not match with expected full url: %s", result, fullUrl)
1596+
}
1597+
})
1598+
}
1599+
}
1600+
1601+
func TestExpandStoragePoolUrl_noProjectAndZoneFromConfigAndData(t *testing.T) {
1602+
config := &transport_tpg.Config{
1603+
ComputeBasePath: "https://www.googleapis.com/compute/v1/",
1604+
}
1605+
1606+
data := &tpgresource.ResourceDataMock{}
1607+
1608+
name := "test-storage-pool"
1609+
zoneUrl := "zones/test-zone/storagePools/" + name
1610+
projectUrl := "projects/test-project/" + zoneUrl
1611+
fullUrl := config.ComputeBasePath + projectUrl
1612+
1613+
cases := []struct {
1614+
name string
1615+
inputStr string
1616+
}{
1617+
{
1618+
name: "full url",
1619+
inputStr: fullUrl,
1620+
},
1621+
{
1622+
name: "project/{project}/zones/{zone}/storagePools/{storagePool}",
1623+
inputStr: projectUrl,
1624+
},
1625+
{
1626+
name: "/project/{project}/zones/{zone}/storagePools/{storagePool}",
1627+
inputStr: "/" + projectUrl,
1628+
},
1629+
{
1630+
name: "zones/{zone}/storagePools/{storagePool}",
1631+
inputStr: zoneUrl,
1632+
},
1633+
{
1634+
name: "/zones/{zone}/storagePools/{storagePool}",
1635+
inputStr: "/" + zoneUrl,
1636+
},
1637+
{
1638+
name: "{storagePool}",
1639+
inputStr: name,
1640+
},
1641+
{
1642+
name: "/{storagePool}",
1643+
inputStr: "/" + name,
1644+
},
1645+
}
1646+
1647+
for _, tc := range cases {
1648+
tc := tc
1649+
t.Run(tc.name, func(t *testing.T) {
1650+
t.Parallel()
1651+
_, err := tpgcompute.ExpandStoragePoolUrl(tc.inputStr, data, config)
1652+
if err == nil {
1653+
t.Fatal("Should return error when no project and zone available from config or resource data")
1654+
}
1655+
})
1656+
}
1657+
}
1658+
14521659
func TestAccComputeDisk_accessModeSpecified(t *testing.T) {
14531660
t.Parallel()
14541661

google/services/compute/resource_compute_instance.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2974,7 +2974,11 @@ func expandBootDisk(d *schema.ResourceData, config *transport_tpg.Config, projec
29742974
}
29752975

29762976
if v, ok := d.GetOk("boot_disk.0.initialize_params.0.storage_pool"); ok {
2977-
disk.InitializeParams.StoragePool = v.(string)
2977+
storagePoolUrl, err := expandStoragePool(v, d, config)
2978+
if err != nil {
2979+
return nil, fmt.Errorf("Error resolving storage pool name '%s': '%s'", v.(string), err)
2980+
}
2981+
disk.InitializeParams.StoragePool = storagePoolUrl.(string)
29782982
}
29792983
}
29802984

@@ -3073,6 +3077,10 @@ func flattenScratchDisk(disk *compute.AttachedDisk) map[string]interface{} {
30733077
return result
30743078
}
30753079

3080+
func expandStoragePool(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
3081+
return ExpandStoragePoolUrl(v, d, config)
3082+
}
3083+
30763084
func hash256(raw string) (string, error) {
30773085
decoded, err := base64.StdEncoding.DecodeString(raw)
30783086
if err != nil {

google/services/compute/resource_compute_instance_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9171,6 +9171,28 @@ func TestAccComputeInstance_bootDisk_storagePoolSpecified(t *testing.T) {
91719171
})
91729172
}
91739173

9174+
func TestAccComputeInstance_bootDisk_storagePoolSpecified_nameOnly(t *testing.T) {
9175+
t.Parallel()
9176+
9177+
instanceName := fmt.Sprintf("tf-test-instance-%s", acctest.RandString(t, 10))
9178+
acctest.BootstrapComputeStoragePool(t, "basic-2", "hyperdisk-balanced")
9179+
9180+
acctest.VcrTest(t, resource.TestCase{
9181+
PreCheck: func() { acctest.AccTestPreCheck(t) },
9182+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
9183+
Steps: []resource.TestStep{
9184+
{
9185+
Config: testAccComputeInstance_bootDisk_storagePoolSpecified(instanceName, "tf-bootstrap-storage-pool-hyperdisk-balanced-basic-2", envvar.GetTestZoneFromEnv()),
9186+
},
9187+
{
9188+
ResourceName: "google_compute_instance.foobar",
9189+
ImportState: true,
9190+
ImportStateVerify: true,
9191+
},
9192+
},
9193+
})
9194+
}
9195+
91749196
func testAccComputeInstance_bootDisk_storagePoolSpecified(instanceName, storagePoolUrl, zone string) string {
91759197
return fmt.Sprintf(`
91769198
data "google_compute_image" "my_image" {

website/docs/r/compute_disk.html.markdown

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,10 +265,12 @@ The following arguments are supported:
265265

266266
* `storage_pool` -
267267
(Optional)
268-
The URL of the storage pool in which the new disk is created.
268+
The URL or the name of the storage pool in which the new disk is created.
269269
For example:
270270
* https://www.googleapis.com/compute/v1/projects/{project}/zones/{zone}/storagePools/{storagePool}
271271
* /projects/{project}/zones/{zone}/storagePools/{storagePool}
272+
* /zones/{zone}/storagePools/{storagePool}
273+
* /{storagePool}
272274

273275
* `access_mode` -
274276
(Optional)

website/docs/r/compute_instance.html.markdown

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,10 +326,12 @@ is desired, you will need to modify your state file manually using
326326
* `enable_confidential_compute` - (Optional) Whether this disk is using confidential compute mode.
327327
Note: Only supported on hyperdisk skus, disk_encryption_key is required when setting to true.
328328

329-
* `storage_pool` - (Optional) The URL of the storage pool in which the new disk is created.
329+
* `storage_pool` - (Optional) The URL or the name of the storage pool in which the new disk is created.
330330
For example:
331331
* https://www.googleapis.com/compute/v1/projects/{project}/zones/{zone}/storagePools/{storagePool}
332332
* /projects/{project}/zones/{zone}/storagePools/{storagePool}
333+
* /zones/{zone}/storagePools/{storagePool}
334+
* /{storagePool}
333335

334336
<a name="nested_scratch_disk"></a>The `scratch_disk` block supports:
335337

0 commit comments

Comments
 (0)