Skip to content

Commit 01bf61d

Browse files
authored
Add identity_ec2_credential resource* allows creating EC2 credentials for S3-compatible S3/RadowGW endpoints (terraform-provider-openstack#1033)
Co-authored-by: Pavel Basov <pbasov@mirantis.com>
1 parent 574fbd8 commit 01bf61d

12 files changed

Lines changed: 623 additions & 0 deletions

File tree

‎go.sum‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ github.com/gophercloud/gophercloud v0.12.0 h1:mZrie07npp6ODiwHZolTicr5jV8Ogn43Av
7777
github.com/gophercloud/gophercloud v0.12.0/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss=
7878
github.com/gophercloud/utils v0.0.0-20200423144003-7c72efc7435d h1:fduaPzWwIfvOMLuHk2Al3GZH0XbUqG8MbElPop+Igzs=
7979
github.com/gophercloud/utils v0.0.0-20200423144003-7c72efc7435d/go.mod h1:ehWUbLQJPqS0Ep+CxeD559hsm9pthPXadJNKwZkp43w=
80+
github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c h1:iawx2ojEQA7c+GmkaVO5sN+k8YONibXyDO8RlsC+1bs=
8081
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
8182
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
8283
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package openstack
2+
3+
import (
4+
"testing"
5+
6+
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
7+
)
8+
9+
func TestAccIdentityV3Ec2Credential_importBasic(t *testing.T) {
10+
resourceName := "openstack_identity_ec2_credential_v3.ec2_cred_1"
11+
12+
resource.Test(t, resource.TestCase{
13+
PreCheck: func() { testAccPreCheck(t) },
14+
Providers: testAccProviders,
15+
CheckDestroy: testAccCheckIdentityV3Ec2CredentialDestroy,
16+
Steps: []resource.TestStep{
17+
{
18+
Config: testAccIdentityV3Ec2Credential_basic,
19+
},
20+
21+
{
22+
ResourceName: resourceName,
23+
ImportState: true,
24+
ImportStateVerify: true,
25+
ImportStateVerifyIgnore: []string{"secret", "project_id"},
26+
},
27+
},
28+
})
29+
}

‎openstack/provider.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ func Provider() terraform.ResourceProvider {
331331
"openstack_identity_user_v3": resourceIdentityUserV3(),
332332
"openstack_identity_group_v3": resourceIdentityGroupV3(),
333333
"openstack_identity_application_credential_v3": resourceIdentityApplicationCredentialV3(),
334+
"openstack_identity_ec2_credential_v3": resourceIdentityEc2CredentialV3(),
334335
"openstack_images_image_v2": resourceImagesImageV2(),
335336
"openstack_images_image_access_v2": resourceImagesImageAccessV2(),
336337
"openstack_images_image_access_accept_v2": resourceImagesImageAccessAcceptV2(),
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
package openstack
2+
3+
import (
4+
"fmt"
5+
"log"
6+
7+
"github.com/gophercloud/gophercloud"
8+
"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
9+
tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
10+
"github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2credentials"
11+
tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
12+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
13+
)
14+
15+
func resourceIdentityEc2CredentialV3() *schema.Resource {
16+
return &schema.Resource{
17+
Create: resourceIdentityEc2CredentialV3Create,
18+
Read: resourceIdentityEc2CredentialV3Read,
19+
Delete: resourceIdentityEc2CredentialV3Delete,
20+
Importer: &schema.ResourceImporter{
21+
State: schema.ImportStatePassthrough,
22+
},
23+
24+
Schema: map[string]*schema.Schema{
25+
"region": {
26+
Type: schema.TypeString,
27+
Optional: true,
28+
Computed: true,
29+
ForceNew: true,
30+
},
31+
32+
"access": {
33+
Type: schema.TypeString,
34+
Computed: true,
35+
ForceNew: true,
36+
},
37+
38+
"secret": {
39+
Type: schema.TypeString,
40+
Computed: true,
41+
Sensitive: true,
42+
ForceNew: true,
43+
},
44+
45+
"project_id": {
46+
Type: schema.TypeString,
47+
Optional: true,
48+
Computed: true,
49+
ForceNew: true,
50+
},
51+
52+
"user_id": {
53+
Type: schema.TypeString,
54+
Optional: true,
55+
Computed: true,
56+
ForceNew: true,
57+
},
58+
59+
"trust_id": {
60+
Type: schema.TypeString,
61+
Computed: true,
62+
ForceNew: true,
63+
},
64+
},
65+
}
66+
}
67+
68+
func resourceIdentityEc2CredentialV3Create(d *schema.ResourceData, meta interface{}) error {
69+
config := meta.(*Config)
70+
71+
identityClient, err := config.IdentityV3Client(GetRegion(d, config))
72+
if err != nil {
73+
return fmt.Errorf("Error creating OpenStack identity client: %s", err)
74+
}
75+
76+
user, project, err := GetTokenInfo(identityClient)
77+
if err != nil {
78+
return fmt.Errorf("Error getting token info: %s", err)
79+
}
80+
81+
var tenantID string
82+
// Set tenant to user value if provided, get one from token otherwise
83+
if definedProject, ok := d.GetOk("project_id"); ok {
84+
tenantID = definedProject.(string)
85+
} else {
86+
tenantID = project
87+
}
88+
89+
createOpts := ec2credentials.CreateOpts{
90+
TenantID: tenantID,
91+
}
92+
93+
var userID string
94+
// Set userid to defined tf value, use one from token otherwise
95+
if definedUser, ok := d.GetOk("user_id"); ok {
96+
userID = definedUser.(string)
97+
} else {
98+
userID = user
99+
}
100+
101+
log.Printf("[DEBUG] openstack_identity_ec2_credential_v3 create options: %#v", createOpts)
102+
103+
ec2Credential, err := ec2credentials.Create(identityClient, userID, createOpts).Extract()
104+
105+
if err != nil {
106+
if v, ok := err.(gophercloud.ErrDefault404); ok {
107+
return fmt.Errorf("Error creating openstack_identity_ec2_credential_v3: %s", v.ErrUnexpectedResponseCode.Body)
108+
}
109+
return fmt.Errorf("Error creating openstack_identity_ec2_credential_v3: %s", err)
110+
}
111+
112+
d.SetId(ec2Credential.Access)
113+
114+
d.Set("access", ec2Credential.Access)
115+
d.Set("secret", ec2Credential.Secret)
116+
d.Set("user_id", ec2Credential.UserID)
117+
d.Set("project_id", ec2Credential.TenantID)
118+
d.Set("trust_id", ec2Credential.TrustID)
119+
120+
return resourceIdentityEc2CredentialV3Read(d, meta)
121+
}
122+
123+
func resourceIdentityEc2CredentialV3Read(d *schema.ResourceData, meta interface{}) error {
124+
config := meta.(*Config)
125+
identityClient, err := config.IdentityV3Client(GetRegion(d, config))
126+
if err != nil {
127+
return fmt.Errorf("Error creating OpenStack identity client: %s", err)
128+
}
129+
130+
user, _, err := GetTokenInfo(identityClient)
131+
if err != nil {
132+
return fmt.Errorf("Error getting token info: %s", err)
133+
}
134+
135+
var userID string
136+
// Set userid to defined tf value, use one from token otherwise
137+
if definedUser, ok := d.GetOk("user_id"); ok {
138+
userID = definedUser.(string)
139+
} else {
140+
userID = user
141+
}
142+
143+
ec2Credential, err := ec2credentials.Get(identityClient, userID, d.Id()).Extract()
144+
if err != nil {
145+
return CheckDeleted(d, err, "Error retrieving openstack_identity_ec2_credential_v3")
146+
}
147+
148+
log.Printf("[DEBUG] Retrieved openstack_identity_ec2_credential_v3 %s: %#v", d.Id(), ec2Credential)
149+
150+
d.Set("secret", ec2Credential.Secret)
151+
d.Set("user_id", ec2Credential.UserID)
152+
d.Set("project_id", ec2Credential.TenantID)
153+
d.Set("access", ec2Credential.Access)
154+
d.Set("trust_id", ec2Credential.TrustID)
155+
156+
return nil
157+
}
158+
159+
func resourceIdentityEc2CredentialV3Delete(d *schema.ResourceData, meta interface{}) error {
160+
config := meta.(*Config)
161+
identityClient, err := config.IdentityV3Client(GetRegion(d, config))
162+
if err != nil {
163+
return fmt.Errorf("Error creating OpenStack identity client: %s", err)
164+
}
165+
166+
user, _, err := GetTokenInfo(identityClient)
167+
if err != nil {
168+
return fmt.Errorf("Error getting token info: %s", err)
169+
}
170+
171+
var userID string
172+
// Set userid to defined tf value, use one from token otherwise
173+
if definedUser, ok := d.GetOk("user_id"); ok {
174+
userID = definedUser.(string)
175+
} else {
176+
userID = user
177+
}
178+
179+
err = ec2credentials.Delete(identityClient, userID, d.Id()).ExtractErr()
180+
if err != nil {
181+
err = CheckDeleted(d, err, "Error deleting openstack_identity_ec2_credential_v3")
182+
if err != nil {
183+
return err
184+
}
185+
}
186+
return nil
187+
}
188+
189+
func GetTokenInfo(sc *gophercloud.ServiceClient) (string, string, error) {
190+
r := sc.ProviderClient.GetAuthResult()
191+
switch r := r.(type) {
192+
case tokens2.CreateResult:
193+
user, project, err := GetTokenInfoV2(r)
194+
if err != nil {
195+
return "", "", err
196+
}
197+
return user, project, nil
198+
case tokens3.CreateResult:
199+
user, project, err := GetTokenInfoV3(r)
200+
if err != nil {
201+
return "", "", err
202+
}
203+
return user, project, nil
204+
default:
205+
return "", "", fmt.Errorf("got unexpected AuthResult type %t", r)
206+
}
207+
}
208+
209+
func GetTokenInfoV3(t tokens3.CreateResult) (string, string, error) {
210+
user, err := t.ExtractUser()
211+
if err != nil {
212+
return "", "", err
213+
}
214+
project, err := t.ExtractProject()
215+
if err != nil {
216+
return "", "", err
217+
}
218+
return user.ID, project.ID, nil
219+
}
220+
221+
func GetTokenInfoV2(t tokens2.CreateResult) (string, string, error) {
222+
var tokeninfo struct {
223+
Access struct {
224+
Token struct {
225+
Expires string `json:"expires"`
226+
ID string `json:"id"`
227+
Tenant tenants.Tenant `json:"tenant"`
228+
} `json:"token"`
229+
User tokens2.User `json:"user"`
230+
} `json:"access"`
231+
}
232+
233+
err := t.ExtractInto(&tokeninfo)
234+
if err != nil {
235+
return "", "", err
236+
}
237+
return tokeninfo.Access.User.ID, tokeninfo.Access.Token.ID, nil
238+
}

0 commit comments

Comments
 (0)