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

Identity V3: Add application credential resource #660

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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/fatih/color v1.6.0 // indirect
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
github.com/gophercloud/gophercloud v0.0.0-20190212181753-892256c46858
github.com/gophercloud/gophercloud v0.0.0-20190215023200-9e57e2f8ff0c
github.com/gophercloud/utils v0.0.0-20190212203534-6f24f46ce3c9
github.com/hashicorp/go-getter v0.0.0-20180425224130-3f60ec5cfbb2 // indirect
github.com/hashicorp/go-hclog v0.0.0-20180402200405-69ff559dc25f // indirect
Expand Down
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,9 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO
github.com/googleapis/gax-go v0.0.0-20161107002406-da06d194a00e h1:CYRpN206UTHUinz3VJoLaBdy1gEGeJNsqT0mvswDcMw=
github.com/googleapis/gax-go v0.0.0-20161107002406-da06d194a00e/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/gophercloud/gophercloud v0.0.0-20190208042652-bc37892e1968/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
github.com/gophercloud/gophercloud v0.0.0-20190212181753-892256c46858 h1:QBK4YvYPJGsghVtVJcEZcQ70UNGFnWUCjtv2NTcEFHA=
github.com/gophercloud/gophercloud v0.0.0-20190212181753-892256c46858/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gophercloud/gophercloud v0.0.0-20190215023200-9e57e2f8ff0c h1:n8bk/luCIxJX+0emWRexjWxuR4CRfMWQuAZYjzkq5Ck=
github.com/gophercloud/gophercloud v0.0.0-20190215023200-9e57e2f8ff0c/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gophercloud/utils v0.0.0-20190128072930-fbb6ab446f01/go.mod h1:wjDF8z83zTeg5eMLml5EBSlAhbF7G8DobyI1YsMuyzw=
github.com/gophercloud/utils v0.0.0-20190212203534-6f24f46ce3c9 h1:frK+RgSyzotPwHzxlbGO9aeM+zoQ4LgUfQbbynAAavw=
github.com/gophercloud/utils v0.0.0-20190212203534-6f24f46ce3c9/go.mod h1:95GkZLE4Nob0I9L4qW5dWmyiekLwK3HsVAOV13XY9CY=
Expand Down
2 changes: 1 addition & 1 deletion openstack/data_source_openstack_identity_auth_scope_v3.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func dataSourceIdentityAuthScopeV3Read(d *schema.ResourceData, meta interface{})
}

d.Set("user_name", user.Name)
d.Set("user_id", user.Name)
d.Set("user_id", user.ID)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you think, this can break anything, let me know - I'll remove this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can constitute as a legitimate bug.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it ok to leave it here, or should I create a dedicated PR?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should really be a dedicated PR since it's has nothing to do with this PR and will have a changelog entry. We can leave this here, but something to keep in mind for future changes.

d.Set("user_domain_name", user.Domain.Name)
d.Set("user_domain_id", user.Domain.ID)

Expand Down
21 changes: 21 additions & 0 deletions openstack/identity_application_credential_v3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package openstack

import (
"github.com/gophercloud/gophercloud/openstack/identity/v3/applicationcredentials"
)

func flattenIdentityApplicationCredentialRolesV3(roles []applicationcredentials.Role) []string {
var res []string
for _, role := range roles {
res = append(res, role.Name)
}
return res
}

func expandIdentityApplicationCredentialRolesV3(roles []interface{}) []applicationcredentials.Role {
var res []applicationcredentials.Role
for _, role := range roles {
res = append(res, applicationcredentials.Role{Name: role.(string)})
}
return res
}
43 changes: 43 additions & 0 deletions openstack/identity_application_credential_v3_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package openstack

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/gophercloud/gophercloud/openstack/identity/v3/applicationcredentials"
)

func TestFlattenIdentityApplicationCredentialRolesV3(t *testing.T) {
role1 := applicationcredentials.Role{
ID: "123",
Name: "foo",
}
role2 := applicationcredentials.Role{
ID: "321",
Name: "bar",
}

roles := []applicationcredentials.Role{role1, role2}

expected := []string{"foo", "bar"}

actual := flattenIdentityApplicationCredentialRolesV3(roles)
assert.Equal(t, expected, actual)
}

func TestExpandIdentityApplicationCredentialRolesV3(t *testing.T) {
role1 := applicationcredentials.Role{
Name: "foo",
}
role2 := applicationcredentials.Role{
Name: "bar",
}

roles := []interface{}{role1.Name, role2.Name}

expected := []applicationcredentials.Role{role1, role2}

actual := expandIdentityApplicationCredentialRolesV3(roles)
assert.Equal(t, expected, actual)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package openstack

import (
"testing"

"github.com/hashicorp/terraform/helper/resource"
)

func TestAccIdentityV3ApplicationCredential_importBasic(t *testing.T) {
resourceName := "openstack_identity_application_credential_v3.app_cred_1"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckIdentityV3ApplicationCredentialDestroy,
Steps: []resource.TestStep{
{
Config: testAccIdentityV3ApplicationCredential_basic,
},

{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"secret"},
},
},
})
}

func TestAccIdentityV3ApplicationCredential_importCustomSecret(t *testing.T) {
resourceName := "openstack_identity_application_credential_v3.app_cred_1"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckIdentityV3ApplicationCredentialDestroy,
Steps: []resource.TestStep{
{
Config: testAccIdentityV3ApplicationCredential_custom_secret,
},

{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"secret"},
},
},
})
}
1 change: 1 addition & 0 deletions openstack/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ func Provider() terraform.ResourceProvider {
"openstack_identity_role_v3": resourceIdentityRoleV3(),
"openstack_identity_role_assignment_v3": resourceIdentityRoleAssignmentV3(),
"openstack_identity_user_v3": resourceIdentityUserV3(),
"openstack_identity_application_credential_v3": resourceIdentityApplicationCredentialV3(),
"openstack_images_image_v2": resourceImagesImageV2(),
"openstack_lb_member_v1": resourceLBMemberV1(),
"openstack_lb_monitor_v1": resourceLBMonitorV1(),
Expand Down
191 changes: 191 additions & 0 deletions openstack/resource_openstack_identity_application_credential_v3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package openstack

import (
"fmt"
"log"
"time"

"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack/identity/v3/applicationcredentials"
"github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
)

func resourceIdentityApplicationCredentialV3() *schema.Resource {
return &schema.Resource{
Create: resourceIdentityApplicationCredentialV3Create,
Read: resourceIdentityApplicationCredentialV3Read,
Delete: resourceIdentityApplicationCredentialV3Delete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

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

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

"description": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"unrestricted": {
Type: schema.TypeBool,
Optional: true,
Default: false,
ForceNew: true,
},

"secret": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Sensitive: true,
ForceNew: true,
},

"project_id": {
Type: schema.TypeString,
Computed: true,
ForceNew: true,
},

"roles": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},

"expires_at": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validation.ValidateRFC3339TimeString,
},
},
}
}

func resourceIdentityApplicationCredentialV3Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
identityClient, err := config.identityV3Client(GetRegion(d, config))
if err != nil {
return fmt.Errorf("Error creating OpenStack identity client: %s", err)
}

token := tokens.Get(identityClient, config.OsClient.TokenID)
if token.Err != nil {
return token.Err
}

user, err := token.ExtractUser()
if err != nil {
return err
}

createOpts := applicationcredentials.CreateOpts{
Name: d.Get("name").(string),
Description: d.Get("description").(string),
Unrestricted: d.Get("unrestricted").(bool),
Roles: expandIdentityApplicationCredentialRolesV3(d.Get("roles").(*schema.Set).List()),
ExpiresAt: d.Get("expires_at").(string),
}

log.Printf("[DEBUG] openstack_identity_application_credential_v3 create options: %#v", createOpts)

createOpts.Secret = d.Get("secret").(string)

applicationCredential, err := applicationcredentials.Create(identityClient, user.ID, createOpts).Extract()
if err != nil {
if v, ok := err.(gophercloud.ErrDefault404); ok {
return fmt.Errorf("Error creating openstack_identity_application_credential_v3: %s", v.ErrUnexpectedResponseCode.Body)
}
return fmt.Errorf("Error creating openstack_identity_application_credential_v3: %s", err)
}

d.SetId(applicationCredential.ID)

// Secret is returned only once
d.Set("secret", applicationCredential.Secret)

return resourceIdentityApplicationCredentialV3Read(d, meta)
}

func resourceIdentityApplicationCredentialV3Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
identityClient, err := config.identityV3Client(GetRegion(d, config))
if err != nil {
return fmt.Errorf("Error creating OpenStack identity client: %s", err)
}

token := tokens.Get(identityClient, config.OsClient.TokenID)
if token.Err != nil {
return token.Err
}

user, err := token.ExtractUser()
if err != nil {
return err
}

applicationCredential, err := applicationcredentials.Get(identityClient, user.ID, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "Error retrieving openstack_identity_application_credential_v3")
}

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

d.Set("name", applicationCredential.Name)
d.Set("description", applicationCredential.Description)
d.Set("unrestricted", applicationCredential.Unrestricted)
d.Set("roles", flattenIdentityApplicationCredentialRolesV3(applicationCredential.Roles))
d.Set("project_id", applicationCredential.ProjectID)
d.Set("region", GetRegion(d, config))

if applicationCredential.ExpiresAt == (time.Time{}) {
d.Set("expires_at", "")
} else {
d.Set("expires_at", applicationCredential.ExpiresAt.UTC().Format(time.RFC3339))
}

return nil
}

func resourceIdentityApplicationCredentialV3Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
identityClient, err := config.identityV3Client(GetRegion(d, config))
if err != nil {
return fmt.Errorf("Error creating OpenStack identity client: %s", err)
}

token := tokens.Get(identityClient, config.OsClient.TokenID)
if token.Err != nil {
return token.Err
}

user, err := token.ExtractUser()
if err != nil {
return err
}

err = applicationcredentials.Delete(identityClient, user.ID, d.Id()).ExtractErr()
if err != nil {
return CheckDeleted(d, err, "Error deleting openstack_identity_application_credential_v3")
}

return nil
}
Loading