Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add openstack_blockstorage_qos_v3 #1325

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/terraform-provider-openstack/terraform-provider-openstack
go 1.17

require (
github.com/gophercloud/gophercloud v0.23.0
github.com/gophercloud/gophercloud v0.23.1-0.20211129155426-97dea84b37a5
github.com/gophercloud/utils v0.0.0-20210909165623-d7085207ff6d
github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.1
github.com/mitchellh/go-homedir v1.1.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
github.com/gophercloud/gophercloud v0.20.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4=
github.com/gophercloud/gophercloud v0.23.0 h1:I3P10oKlGu3DHP9PrEWMr1ya+/+3Rc9uRHNkRZ9wO7g=
github.com/gophercloud/gophercloud v0.23.0/go.mod h1:MRw6uyLj8uCGbIvBlqL7QW67t0QtNZnzydUzewo1Ioc=
github.com/gophercloud/gophercloud v0.23.1-0.20211129155426-97dea84b37a5 h1:EckX+4F7gL2lJgrQaqmZYomKv5EupJgzviarTTxDg4g=
github.com/gophercloud/gophercloud v0.23.1-0.20211129155426-97dea84b37a5/go.mod h1:MRw6uyLj8uCGbIvBlqL7QW67t0QtNZnzydUzewo1Ioc=
github.com/gophercloud/utils v0.0.0-20210909165623-d7085207ff6d h1:0Wsi5dvUuPF6dVn/CNfEA4xLxmaEtOt7tV2HD16xIf8=
github.com/gophercloud/utils v0.0.0-20210909165623-d7085207ff6d/go.mod h1:qOGlfG6OIJ193/c3Xt/XjOfHataNZdQcVgiu93LxBUM=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
Expand Down
31 changes: 31 additions & 0 deletions openstack/import_openstack_blockstorage_qos_v3_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package openstack

import (
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccBlockStorageQosV3_importBasic(t *testing.T) {
resourceName := "openstack_blockstorage_qos_v3.qos"

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
testAccPreCheckAdminOnly(t)
},
ProviderFactories: testAccProviders,
CheckDestroy: testAccCheckBlockStorageQosV3Destroy,
Steps: []resource.TestStep{
{
Config: testAccBlockStorageQosV3Basic,
},

{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
1 change: 1 addition & 0 deletions openstack/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ func Provider() *schema.Provider {
},

ResourcesMap: map[string]*schema.Resource{
"openstack_blockstorage_qos_v3": resourceBlockStorageQosV3(),
"openstack_blockstorage_quotaset_v2": resourceBlockStorageQuotasetV2(),
"openstack_blockstorage_quotaset_v3": resourceBlockStorageQuotasetV3(),
"openstack_blockstorage_volume_v1": resourceBlockStorageVolumeV1(),
Expand Down
173 changes: 173 additions & 0 deletions openstack/resource_openstack_blockstorage_qos_v3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package openstack

import (
"context"
"log"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"

"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos"
)

func resourceBlockStorageQosV3() *schema.Resource {
return &schema.Resource{
CreateContext: resourceBlockStorageQosV3Create,
ReadContext: resourceBlockStorageQosV3Read,
UpdateContext: resourceBlockStorageQosV3Update,
DeleteContext: resourceBlockStorageQosV3Delete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Schema: map[string]*schema.Schema{
"region": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},

"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"consumer": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{
"front-end", "back-end", "both",
}, false),
},

"specs": {
Type: schema.TypeMap,
Optional: true,
},
},
}
}

func resourceBlockStorageQosV3Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
config := meta.(*Config)
blockStorageClient, err := config.BlockStorageV3Client(GetRegion(d, config))
if err != nil {
return diag.Errorf("Error creating OpenStack block storage client: %s", err)
}

name := d.Get("name").(string)
consumer := qos.QoSConsumer(d.Get("consumer").(string))
specs := d.Get("specs").(map[string]interface{})
createOpts := qos.CreateOpts{
Name: name,
Consumer: consumer,
Specs: expandToMapStringString(specs),
}

log.Printf("[DEBUG] openstack_blockstorage_qos_v3 create options: %#v", createOpts)
qosRes, err := qos.Create(blockStorageClient, &createOpts).Extract()
if err != nil {
return diag.Errorf("Error creating openstack_blockstorage_qos_v3 %s: %s", name, err)
}

d.SetId(qosRes.ID)

return resourceBlockStorageQosV3Read(ctx, d, meta)
}

func resourceBlockStorageQosV3Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
config := meta.(*Config)
blockStorageClient, err := config.BlockStorageV3Client(GetRegion(d, config))
if err != nil {
return diag.Errorf("Error creating OpenStack block storage client: %s", err)
}

qosRes, err := qos.Get(blockStorageClient, d.Id()).Extract()
if err != nil {
return diag.FromErr(CheckDeleted(d, err, "Error retrieving openstack_blockstorage_qos_v3"))
}

log.Printf("[DEBUG] Retrieved openstack_blockstorage_qos_v3 %s: %#v", d.Id(), qosRes)

d.Set("region", GetRegion(d, config))
d.Set("name", qosRes.Name)
d.Set("consumer", qosRes.Consumer)

if err := d.Set("specs", qosRes.Specs); err != nil {
log.Printf("[WARN] Unable to set specs for openstack_blockstorage_qos_v3 %s: %s", d.Id(), err)
}

return nil
}

func resourceBlockStorageQosV3Update(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
config := meta.(*Config)
blockStorageClient, err := config.BlockStorageV3Client(GetRegion(d, config))
if err != nil {
return diag.Errorf("Error creating OpenStack block storage client: %s", err)
}

hasChange := false
var updateOpts qos.UpdateOpts

if d.HasChange("consumer") {
hasChange = true
consumer := qos.QoSConsumer(d.Get("consumer").(string))
updateOpts.Consumer = consumer
}

if d.HasChange("specs") {
oldSpecsRaw, newSpecsRaw := d.GetChange("specs")

// Delete all old specs.
var deleteKeys qos.DeleteKeysOpts
for oldKey := range oldSpecsRaw.(map[string]interface{}) {
deleteKeys = append(deleteKeys, oldKey)
}
err = qos.DeleteKeys(blockStorageClient, d.Id(), deleteKeys).ExtractErr()
if err != nil {
return diag.Errorf("Error deleting specs for openstack_blockstorage_qos_v3 %s: %s", d.Id(), err)
}

// Add new specs to UpdateOpts
newSpecs := expandToMapStringString(newSpecsRaw.(map[string]interface{}))
if len(newSpecs) > 0 {
hasChange = true
updateOpts.Specs = newSpecs
}
}

if hasChange {
_, err = qos.Update(blockStorageClient, d.Id(), updateOpts).Extract()
if err != nil {
return diag.Errorf("Error updating openstack_blockstorage_qos_v3 %s: %s", d.Id(), err)
}
}

return resourceBlockStorageQosV3Read(ctx, d, meta)
}

func resourceBlockStorageQosV3Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
config := meta.(*Config)
blockStorageClient, err := config.BlockStorageV3Client(GetRegion(d, config))
if err != nil {
return diag.Errorf("Error creating OpenStack block storage client: %s", err)
}

// remove all associations first
err = qos.DisassociateAll(blockStorageClient, d.Id()).ExtractErr()
if err != nil {
return diag.FromErr(CheckDeleted(d, err, "Error deleting openstack_blockstorage_qos_v3 associations"))
}

// Delete the QoS itself
err = qos.Delete(blockStorageClient, d.Id(), qos.DeleteOpts{}).ExtractErr()
if err != nil {
return diag.FromErr(CheckDeleted(d, err, "Error deleting openstack_blockstorage_qos_v3"))
}

return nil
}
153 changes: 153 additions & 0 deletions openstack/resource_openstack_blockstorage_qos_v3_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package openstack

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"

"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos"
)

func TestAccBlockStorageQosV3_basic(t *testing.T) {
var qosTest qos.QoS

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
testAccPreCheckAdminOnly(t)
},
ProviderFactories: testAccProviders,
CheckDestroy: testAccCheckBlockStorageQosV3Destroy,
Steps: []resource.TestStep{
{
Config: testAccBlockStorageQosV3Basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckBlockStorageQosV3Exists("openstack_blockstorage_qos_v3.qos", &qosTest),
resource.TestCheckResourceAttr(
"openstack_blockstorage_qos_v3.qos", "name", "foo"),
resource.TestCheckResourceAttr(
"openstack_blockstorage_qos_v3.qos", "consumer", "front-end"),
resource.TestCheckResourceAttr(
"openstack_blockstorage_qos_v3.qos", "specs.%", "1"),
resource.TestCheckResourceAttr(
"openstack_blockstorage_qos_v3.qos", "specs.read_iops_sec", "20000"),
),
},
{
Config: testAccBlockStorageQosV3Update1,
Check: resource.ComposeTestCheckFunc(
testAccCheckBlockStorageQosV3Exists("openstack_blockstorage_qos_v3.qos", &qosTest),
resource.TestCheckResourceAttr(
"openstack_blockstorage_qos_v3.qos", "name", "foo"),
resource.TestCheckResourceAttr(
"openstack_blockstorage_qos_v3.qos", "consumer", "back-end"),
resource.TestCheckResourceAttr(
"openstack_blockstorage_qos_v3.qos", "specs.%", "2"),
resource.TestCheckResourceAttr(
"openstack_blockstorage_qos_v3.qos", "specs.read_iops_sec", "40000"),
resource.TestCheckResourceAttr(
"openstack_blockstorage_qos_v3.qos", "specs.write_iops_sec", "40000"),
),
},
{
Config: testAccBlockStorageQosV3Update2,
Check: resource.ComposeTestCheckFunc(
testAccCheckBlockStorageQosV3Exists("openstack_blockstorage_qos_v3.qos", &qosTest),
resource.TestCheckResourceAttr(
"openstack_blockstorage_qos_v3.qos", "name", "foo"),
resource.TestCheckResourceAttr(
"openstack_blockstorage_qos_v3.qos", "consumer", "back-end"),
resource.TestCheckResourceAttr(
"openstack_blockstorage_qos_v3.qos", "specs.%", "0"),
),
},
},
})
}

func testAccCheckBlockStorageQosV3Destroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
blockStorageClient, err := config.BlockStorageV3Client(osRegionName)
if err != nil {
return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
}

for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_blockstorage_qos_v3" {
continue
}

_, err := qos.Get(blockStorageClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("Qos still exists")
}
}

return nil
}

func testAccCheckBlockStorageQosV3Exists(n string, qosTest *qos.QoS) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}

config := testAccProvider.Meta().(*Config)
blockStorageClient, err := config.BlockStorageV3Client(osRegionName)
if err != nil {
return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
}

found, err := qos.Get(blockStorageClient, rs.Primary.ID).Extract()
if err != nil {
return err
}

if found.ID != rs.Primary.ID {
return fmt.Errorf("Qos not found")
}

*qosTest = *found

return nil
}
}

const testAccBlockStorageQosV3Basic = `
resource "openstack_blockstorage_qos_v3" "qos" {
name = "foo"
consumer = "front-end"
specs = {
read_iops_sec = "20000"
}

}
`

const testAccBlockStorageQosV3Update1 = `
resource "openstack_blockstorage_qos_v3" "qos" {
name = "foo"
consumer = "back-end"
specs = {
read_iops_sec = "40000"
write_iops_sec = "40000"
}

}
`

const testAccBlockStorageQosV3Update2 = `
resource "openstack_blockstorage_qos_v3" "qos" {
name = "foo"
consumer = "back-end"
specs = {
}
}
`