tfplan2cai/converters/google/resources/cai/iam_helpers.go (191 lines of code) (raw):
package cai
import (
"encoding/json"
"fmt"
"sort"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgiamresource"
"github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource"
transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport"
cloudresourcemanager "google.golang.org/api/cloudresourcemanager/v1"
)
// ExpandIamPolicyBindings is used in google_<type>_iam_policy resources.
func ExpandIamPolicyBindings(d tpgresource.TerraformResourceData) ([]IAMBinding, error) {
ps := d.Get("policy_data").(string)
var bindings []IAMBinding
// policy_data is (known after apply) in terraform plan, hence an empty string
if ps == "" {
return bindings, nil
}
// The policy string is just a marshaled cloudresourcemanager.Policy.
policy := &cloudresourcemanager.Policy{}
if err := json.Unmarshal([]byte(ps), policy); err != nil {
return nil, fmt.Errorf("Could not unmarshal %s: %v", ps, err)
}
for _, b := range policy.Bindings {
bindings = append(bindings, IAMBinding{
Role: b.Role,
Members: b.Members,
})
}
return bindings, nil
}
// ExpandIamRoleBindings is used in google_<type>_iam_binding resources.
func ExpandIamRoleBindings(d tpgresource.TerraformResourceData) ([]IAMBinding, error) {
var members []string
for _, m := range d.Get("members").(*schema.Set).List() {
members = append(members, m.(string))
}
return []IAMBinding{
{
Role: d.Get("role").(string),
Members: members,
},
}, nil
}
// ExpandIamMemberBindings is used in google_<type>_iam_member resources.
func ExpandIamMemberBindings(d tpgresource.TerraformResourceData) ([]IAMBinding, error) {
return []IAMBinding{
{
Role: d.Get("role").(string),
Members: []string{d.Get("member").(string)},
},
}, nil
}
// MergeIamAssets merges an existing asset with the IAM bindings of an incoming
// Asset.
func MergeIamAssets(
existing, incoming Asset,
MergeBindings func(existing, incoming []IAMBinding) []IAMBinding,
) Asset {
if existing.IAMPolicy != nil {
existing.IAMPolicy.Bindings = MergeBindings(existing.IAMPolicy.Bindings, incoming.IAMPolicy.Bindings)
} else {
existing.IAMPolicy = incoming.IAMPolicy
}
return existing
}
// incoming is the last known state of an asset prior to deletion
func MergeDeleteIamAssets(
existing, incoming Asset,
MergeBindings func(existing, incoming []IAMBinding) []IAMBinding,
) Asset {
if existing.IAMPolicy != nil {
existing.IAMPolicy.Bindings = MergeBindings(existing.IAMPolicy.Bindings, incoming.IAMPolicy.Bindings)
}
return existing
}
// MergeAdditiveBindings adds members to bindings with the same roles and adds new
// bindings for roles that dont exist.
func MergeAdditiveBindings(existing, incoming []IAMBinding) []IAMBinding {
existingIdxs := make(map[string]int)
for i, binding := range existing {
existingIdxs[binding.Role] = i
}
for _, binding := range incoming {
if ei, ok := existingIdxs[binding.Role]; ok {
memberExists := make(map[string]bool)
for _, m := range existing[ei].Members {
memberExists[m] = true
}
for _, m := range binding.Members {
// Only add members that don't exist.
if !memberExists[m] {
existing[ei].Members = append(existing[ei].Members, m)
}
}
} else {
existing = append(existing, binding)
}
}
// Sort members
for i := range existing {
sort.Strings(existing[i].Members)
}
return existing
}
// MergeDeleteAdditiveBindings eliminates listed members from roles in the
// existing list. incoming is the last known state of the bindings being deleted.
func MergeDeleteAdditiveBindings(existing, incoming []IAMBinding) []IAMBinding {
toDelete := make(map[string]struct{})
for _, binding := range incoming {
for _, m := range binding.Members {
key := binding.Role + "-" + m
toDelete[key] = struct{}{}
}
}
var newExisting []IAMBinding
for _, binding := range existing {
var newMembers []string
for _, m := range binding.Members {
key := binding.Role + "-" + m
_, delete := toDelete[key]
if !delete {
newMembers = append(newMembers, m)
}
}
if newMembers != nil {
newExisting = append(newExisting, IAMBinding{
Role: binding.Role,
Members: newMembers,
})
}
}
return newExisting
}
// MergeAuthoritativeBindings clobbers members to bindings with the same roles
// and adds new bindings for roles that dont exist.
func MergeAuthoritativeBindings(existing, incoming []IAMBinding) []IAMBinding {
existingIdxs := make(map[string]int)
for i, binding := range existing {
existingIdxs[binding.Role] = i
}
for _, binding := range incoming {
if ei, ok := existingIdxs[binding.Role]; ok {
existing[ei].Members = binding.Members
} else {
existing = append(existing, binding)
}
}
// Sort members
for i := range existing {
sort.Strings(existing[i].Members)
}
return existing
}
// MergeDeleteAuthoritativeBindings eliminates any bindings with matching roles
// in the existing list. incoming is the last known state of the bindings being
// deleted.
func MergeDeleteAuthoritativeBindings(existing, incoming []IAMBinding) []IAMBinding {
toDelete := make(map[string]struct{})
for _, binding := range incoming {
key := binding.Role
toDelete[key] = struct{}{}
}
var newExisting []IAMBinding
for _, binding := range existing {
key := binding.Role
_, delete := toDelete[key]
if !delete {
newExisting = append(newExisting, binding)
}
}
return newExisting
}
func FetchIamPolicy(
newUpdaterFunc tpgiamresource.NewResourceIamUpdaterFunc,
d tpgresource.TerraformResourceData,
config *transport_tpg.Config,
assetNameTmpl string,
assetType string,
) (Asset, error) {
updater, err := newUpdaterFunc(d, config)
if err != nil {
return Asset{}, err
}
iamPolicy, err := updater.GetResourceIamPolicy()
if transport_tpg.IsGoogleApiErrorWithCode(err, 403) || transport_tpg.IsGoogleApiErrorWithCode(err, 404) {
return Asset{}, ErrResourceInaccessible
}
if err != nil {
return Asset{}, err
}
var bindings []IAMBinding
for _, b := range iamPolicy.Bindings {
bindings = append(
bindings,
IAMBinding{
Role: b.Role,
Members: b.Members,
},
)
}
name, err := AssetName(d, config, assetNameTmpl)
return Asset{
Name: name,
Type: assetType,
IAMPolicy: &IAMPolicy{
Bindings: bindings,
},
}, nil
}