terraform-provider-google/google/resource_google_project_iam_member.go
Paddy d3426d5bc6 Don't set IDs in RMW loops.
We don't need to set the ID to "" in read-modify-write helpers, because
once they're done, we read anyways to update state based on the changes.
And that read checks if the binding/member still exists, and does the
SetId("") if it doesn't.

This way, we stick with state only getting set based on the API state,
not by what we think the state will be.
2017-07-27 14:29:25 -07:00

187 lines
4.8 KiB
Go

package google
import (
"fmt"
"log"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/cloudresourcemanager/v1"
)
func resourceGoogleProjectIamMember() *schema.Resource {
return &schema.Resource{
Create: resourceGoogleProjectIamMemberCreate,
Read: resourceGoogleProjectIamMemberRead,
Delete: resourceGoogleProjectIamMemberDelete,
Schema: map[string]*schema.Schema{
"project": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"role": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"member": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"etag": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceGoogleProjectIamMemberCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
pid, err := getProject(d, config)
if err != nil {
return err
}
// Get the binding in the template
log.Println("[DEBUG]: Reading google_project_iam_member")
p := getResourceIamMember(d)
mutexKV.Lock(projectIamMemberMutexKey(pid, p.Role, p.Members[0]))
defer mutexKV.Unlock(projectIamMemberMutexKey(pid, p.Role, p.Members[0]))
err = projectIamPolicyReadModifyWrite(d, config, pid, func(ep *cloudresourcemanager.Policy) error {
// find the binding
var binding *cloudresourcemanager.Binding
for _, b := range ep.Bindings {
if b.Role != p.Role {
continue
}
binding = b
break
}
if binding == nil {
binding = &cloudresourcemanager.Binding{
Role: p.Role,
Members: p.Members,
}
}
// Merge the bindings together
ep.Bindings = mergeBindings(append(ep.Bindings, p))
return nil
})
if err != nil {
return err
}
d.SetId(pid + "/" + p.Role + "/" + p.Members[0])
return resourceGoogleProjectIamMemberRead(d, meta)
}
func resourceGoogleProjectIamMemberRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
pid, err := getProject(d, config)
if err != nil {
return err
}
eMember := getResourceIamMember(d)
log.Println("[DEBUG]: Retrieving policy for project", pid)
p, err := getProjectIamPolicy(pid, config)
if err != nil {
return err
}
log.Printf("[DEBUG]: Retrieved policy for project %q: %+v\n", pid, p)
var binding *cloudresourcemanager.Binding
for _, b := range p.Bindings {
if b.Role != eMember.Role {
continue
}
binding = b
break
}
if binding == nil {
log.Printf("[DEBUG]: Binding for role %q does not exist in policy of project %q, removing member %q from state.", eMember.Role, pid, eMember.Members[0])
d.SetId("")
return nil
}
var member string
for _, m := range binding.Members {
if m == eMember.Members[0] {
member = m
}
}
if member == "" {
log.Printf("[DEBUG]: Member %q for binding for role %q does not exist in policy of project %q, removing from state.", eMember.Members[0], eMember.Role, pid)
d.SetId("")
return nil
}
d.Set("etag", p.Etag)
d.Set("member", member)
d.Set("role", binding.Role)
return nil
}
func resourceGoogleProjectIamMemberDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
pid, err := getProject(d, config)
if err != nil {
return err
}
member := getResourceIamMember(d)
mutexKV.Lock(projectIamMemberMutexKey(pid, member.Role, member.Members[0]))
defer mutexKV.Unlock(projectIamMemberMutexKey(pid, member.Role, member.Members[0]))
err = projectIamPolicyReadModifyWrite(d, config, pid, func(p *cloudresourcemanager.Policy) error {
bindingToRemove := -1
for pos, b := range p.Bindings {
if b.Role != member.Role {
continue
}
bindingToRemove = pos
break
}
if bindingToRemove < 0 {
log.Printf("[DEBUG]: Binding for role %q does not exist in policy of project %q, so member %q can't be on it.", eMember.Role, pid, eMember.Members[0])
return nil
}
binding := p.Bindings[bindingToRemove]
memberToRemove := -1
for pos, m := range binding.Members {
if m != member.Members[0] {
continue
}
memberToRemove = pos
break
}
if memberToRemove < 0 {
log.Printf("[DEBUG]: Member %q for binding for role %q does not exist in policy of project %q.", member.Members[0], member.Role, pid)
return nil
}
binding.Members = append(binding.Members[:memberToRemove], binding.Members[memberToRemove+1:]...)
p.Bindings[bindingToRemove] = binding
return nil
})
if err != nil {
return err
}
return resourceGoogleProjectIamMemberRead(d, meta)
}
// Get a cloudresourcemanager.Binding from a schema.ResourceData
func getResourceIamMember(d *schema.ResourceData) *cloudresourcemanager.Binding {
return &cloudresourcemanager.Binding{
Members: []string{d.Get("member").(string)},
Role: d.Get("role").(string),
}
}
func projectIamMemberMutexKey(pid, role, member string) string {
return fmt.Sprintf("google-project-iam-member-%s-%s-%s", pid, role, member)
}