mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-09-07 09:36:44 +00:00
![Paddy](/assets/img/avatar_default.png)
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.
184 lines
4.5 KiB
Go
184 lines
4.5 KiB
Go
package google
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
"google.golang.org/api/cloudresourcemanager/v1"
|
|
)
|
|
|
|
func resourceGoogleProjectIamBinding() *schema.Resource {
|
|
return &schema.Resource{
|
|
Create: resourceGoogleProjectIamBindingCreate,
|
|
Read: resourceGoogleProjectIamBindingRead,
|
|
Update: resourceGoogleProjectIamBindingUpdate,
|
|
Delete: resourceGoogleProjectIamBindingDelete,
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
"project": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
ForceNew: true,
|
|
},
|
|
"role": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ForceNew: true,
|
|
},
|
|
"members": {
|
|
Type: schema.TypeSet,
|
|
Required: true,
|
|
Elem: &schema.Schema{
|
|
Type: schema.TypeString,
|
|
},
|
|
},
|
|
"etag": {
|
|
Type: schema.TypeString,
|
|
Computed: true,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func resourceGoogleProjectIamBindingCreate(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_binding")
|
|
p := getResourceIamBinding(d)
|
|
mutexKV.Lock(projectIamBindingMutexKey(pid, p.Role))
|
|
defer mutexKV.Unlock(projectIamBindingMutexKey(pid, p.Role))
|
|
|
|
err = projectIamPolicyReadModifyWrite(d, config, pid, func(ep *cloudresourcemanager.Policy) error {
|
|
// Merge the bindings together
|
|
ep.Bindings = mergeBindings(append(ep.Bindings, p))
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
d.SetId(pid + "/" + p.Role)
|
|
return resourceGoogleProjectIamBindingRead(d, meta)
|
|
}
|
|
|
|
func resourceGoogleProjectIamBindingRead(d *schema.ResourceData, meta interface{}) error {
|
|
config := meta.(*Config)
|
|
pid, err := getProject(d, config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
eBinding := getResourceIamBinding(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 != eBinding.Role {
|
|
continue
|
|
}
|
|
binding = b
|
|
break
|
|
}
|
|
if binding == nil {
|
|
log.Printf("[DEBUG]: Binding for role %q not found in policy for %q, removing from state file.\n", eBinding.Role, pid)
|
|
d.SetId("")
|
|
return nil
|
|
}
|
|
d.Set("etag", p.Etag)
|
|
d.Set("members", binding.Members)
|
|
d.Set("role", binding.Role)
|
|
return nil
|
|
}
|
|
|
|
func resourceGoogleProjectIamBindingUpdate(d *schema.ResourceData, meta interface{}) error {
|
|
config := meta.(*Config)
|
|
pid, err := getProject(d, config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
binding := getResourceIamBinding(d)
|
|
mutexKV.Lock(projectIamBindingMutexKey(pid, binding.Role))
|
|
defer mutexKV.Unlock(projectIamBindingMutexKey(pid, binding.Role))
|
|
|
|
err = projectIamPolicyReadModifyWrite(d, config, pid, func(p *cloudresourcemanager.Policy) error {
|
|
var found bool
|
|
for pos, b := range p.Bindings {
|
|
if b.Role != binding.Role {
|
|
continue
|
|
}
|
|
found = true
|
|
p.Bindings[pos] = binding
|
|
break
|
|
}
|
|
if !found {
|
|
p.Bindings = append(p.Bindings, binding)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return resourceGoogleProjectIamBindingRead(d, meta)
|
|
}
|
|
|
|
func resourceGoogleProjectIamBindingDelete(d *schema.ResourceData, meta interface{}) error {
|
|
config := meta.(*Config)
|
|
pid, err := getProject(d, config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
binding := getResourceIamBinding(d)
|
|
mutexKV.Lock(projectIamBindingMutexKey(pid, binding.Role))
|
|
defer mutexKV.Unlock(projectIamBindingMutexKey(pid, binding.Role))
|
|
|
|
err = projectIamPolicyReadModifyWrite(d, config, pid, func(p *cloudresourcemanager.Policy) error {
|
|
toRemove := -1
|
|
for pos, b := range p.Bindings {
|
|
if b.Role != binding.Role {
|
|
continue
|
|
}
|
|
toRemove = pos
|
|
break
|
|
}
|
|
if toRemove < 0 {
|
|
log.Printf("[DEBUG]: Policy bindings for project %q did not include a binding for role %q", pid, binding.Role)
|
|
return nil
|
|
}
|
|
|
|
p.Bindings = append(p.Bindings[:toRemove], p.Bindings[toRemove+1:]...)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return resourceGoogleProjectIamBindingRead(d, meta)
|
|
}
|
|
|
|
// Get a cloudresourcemanager.Binding from a schema.ResourceData
|
|
func getResourceIamBinding(d *schema.ResourceData) *cloudresourcemanager.Binding {
|
|
members := d.Get("members").(*schema.Set).List()
|
|
return &cloudresourcemanager.Binding{
|
|
Members: convertStringArr(members),
|
|
Role: d.Get("role").(string),
|
|
}
|
|
}
|
|
|
|
func projectIamBindingMutexKey(pid, role string) string {
|
|
return fmt.Sprintf("google-project-iam-binding-%s-%s", pid, role)
|
|
}
|