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

Add resource for GCP Static Account #1094

Merged
merged 15 commits into from
Jul 31, 2021
Merged
Show file tree
Hide file tree
Changes from 10 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
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ require (
github.com/hashicorp/vault/sdk v0.1.14-0.20210526173046-412db2245e81
github.com/mitchellh/go-homedir v1.1.0
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be // indirect
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
)
96 changes: 96 additions & 0 deletions vault/gcp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package vault

import (
"bytes"
"fmt"
"sort"

"github.com/hashicorp/terraform-plugin-sdk/helper/hashcode"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

type GCPBinding struct {
benashz marked this conversation as resolved.
Show resolved Hide resolved
Resource string
Roles []string
}

func gcpSecretFlattenBinding(v interface{}) interface{} {
transformed := schema.NewSet(gcpSecretBindingHash, []interface{}{})
if v == nil {
return transformed
}

rawBindings := v.((map[string]interface{}))
for resource, roles := range rawBindings {
transformed.Add(map[string]interface{}{
"resource": resource,
"roles": schema.NewSet(schema.HashString, roles.([]interface{})),
})
}

return transformed
}

func gcpSecretBindingHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
buf.WriteString(fmt.Sprintf("%s-", m["resource"].(string)))

// We need to make sure to sort the strings below so that we always
// generate the same hash code no matter what is in the set.
if v, ok := m["roles"]; ok {
vs := v.(*schema.Set).List()
s := make([]string, len(vs))
for i, raw := range vs {
s[i] = raw.(string)
}
sort.Strings(s)

for _, v := range s {
buf.WriteString(fmt.Sprintf("%s-", v))
}
}
return hashcode.String(buf.String())
}

func gcpSecretRenderBinding(binding *GCPBinding) string {
output := fmt.Sprintf("resource \"%s\" {\n", binding.Resource)
output = fmt.Sprintf("%s roles = %s\n", output, policyRenderListOfStrings(binding.Roles))
return fmt.Sprintf("%s}\n", output)
}

func gcpSecretRenderBindings(bindings []*GCPBinding) string {
var output string

for i, binding := range bindings {
if i == 0 {
output = fmt.Sprintf("%s", gcpSecretRenderBinding(binding))
} else {
output = fmt.Sprintf("%s\n\n%s", output, gcpSecretRenderBinding(binding))
}
}

return output
}

func gcpSecretRenderBindingsFromData(v interface{}) string {
rawBindings := v.(*schema.Set).List()

bindings := make([]*GCPBinding, len(rawBindings))

for i, binding := range rawBindings {
rawRoles := binding.(map[string]interface{})["roles"].(*schema.Set).List()
roles := make([]string, len(rawRoles))
for j, role := range rawRoles {
roles[j] = role.(string)
}

binding := &GCPBinding{
Resource: binding.(map[string]interface{})["resource"].(string),
Roles: roles,
}
bindings[i] = binding
}

return gcpSecretRenderBindings(bindings)
}
4 changes: 4 additions & 0 deletions vault/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,10 @@ var (
Resource: gcpSecretRolesetResource(),
PathInventory: []string{"/gcp/roleset/{name}"},
},
"vault_gcp_secret_static_account": {
Resource: gcpSecretStaticAccountResource(),
PathInventory: []string{"/gcp/static/{name}"},
lawliet89 marked this conversation as resolved.
Show resolved Hide resolved
},
"vault_cert_auth_backend_role": {
Resource: certAuthBackendRoleResource(),
PathInventory: []string{"/auth/cert/certs/{name}"},
Expand Down
99 changes: 5 additions & 94 deletions vault/resource_gcp_secret_roleset.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package vault

import (
"bytes"
"fmt"
"log"
"regexp"
"sort"
"strings"

"github.com/hashicorp/terraform-plugin-sdk/helper/customdiff"
"github.com/hashicorp/terraform-plugin-sdk/helper/hashcode"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/vault/api"
)
Expand All @@ -19,11 +16,6 @@ var (
gcpSecretRolesetNameFromPathRegex = regexp.MustCompile("^.+/roleset/(.+)$")
)

type Binding struct {
Resource string
Roles []string
}

func gcpSecretRolesetResource() *schema.Resource {
return &schema.Resource{
Create: gcpSecretRolesetCreate,
Expand Down Expand Up @@ -76,7 +68,7 @@ func gcpSecretRolesetResource() *schema.Resource {
"binding": {
Type: schema.TypeSet,
Required: true,
Set: gcpSecretRolesetBindingHash,
Set: gcpSecretBindingHash,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"resource": {
Expand Down Expand Up @@ -107,8 +99,8 @@ func gcpSecretRolesetResource() *schema.Resource {
// Due to https://github.com/hashicorp/terraform/issues/17411
// we cannot use d.HasChange("binding") directly
oldBinding, newBinding := d.GetChange("binding")
oldHcl := renderBindingsFromData(oldBinding)
newHcl := renderBindingsFromData(newBinding)
oldHcl := gcpSecretRenderBindingsFromData(oldBinding)
newHcl := gcpSecretRenderBindingsFromData(newBinding)

return d.HasChange("token_scopes") || oldHcl != newHcl
}),
Expand Down Expand Up @@ -191,7 +183,7 @@ func gcpSecretRolesetRead(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("error reading %s for GCP Secrets backend roleset %q", "project", path)
}

if err := d.Set("binding", gcpSecretRolesetFlattenBinding(resp.Data["bindings"])); err != nil {
if err := d.Set("binding", gcpSecretFlattenBinding(resp.Data["bindings"])); err != nil {
return fmt.Errorf("error reading %s for GCP Secrets backend roleset %q", "binding", path)
}

Expand Down Expand Up @@ -230,23 +222,6 @@ func gcpSecretRolesetDelete(d *schema.ResourceData, meta interface{}) error {
return nil
}

func gcpSecretRolesetFlattenBinding(v interface{}) interface{} {
if v == nil {
return v
}

rawBindings := v.((map[string]interface{}))
transformed := schema.NewSet(gcpSecretRolesetBindingHash, []interface{}{})
for resource, roles := range rawBindings {
transformed.Add(map[string]interface{}{
"resource": resource,
"roles": schema.NewSet(schema.HashString, roles.([]interface{})),
})
}

return transformed
}

func gcpSecretRolesetUpdateFields(d *schema.ResourceData, data map[string]interface{}) {
if v, ok := d.GetOk("secret_type"); ok {
data["secret_type"] = v.(string)
Expand All @@ -261,7 +236,7 @@ func gcpSecretRolesetUpdateFields(d *schema.ResourceData, data map[string]interf
}

if v, ok := d.GetOk("binding"); ok {
bindingsHCL := renderBindingsFromData(v)
bindingsHCL := gcpSecretRenderBindingsFromData(v)
log.Printf("[DEBUG] Rendered GCP Secrets backend roleset bindings HCL:\n%s", bindingsHCL)
data["bindings"] = bindingsHCL
}
Expand All @@ -279,28 +254,6 @@ func gcpSecretRolesetExists(d *schema.ResourceData, meta interface{}) (bool, err
return secret != nil, nil
}

func gcpSecretRolesetBindingHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
buf.WriteString(fmt.Sprintf("%s-", m["resource"].(string)))

// We need to make sure to sort the strings below so that we always
// generate the same hash code no matter what is in the set.
if v, ok := m["roles"]; ok {
vs := v.(*schema.Set).List()
s := make([]string, len(vs))
for i, raw := range vs {
s[i] = raw.(string)
}
sort.Strings(s)

for _, v := range s {
buf.WriteString(fmt.Sprintf("%s-", v))
}
}
return hashcode.String(buf.String())
}

func gcpSecretRolesetPath(backend, roleset string) string {
return strings.Trim(backend, "/") + "/roleset/" + strings.Trim(roleset, "/")
}
Expand All @@ -326,45 +279,3 @@ func gcpSecretRoleSetdRolesetNameFromPath(path string) (string, error) {
}
return res[1], nil
}

func renderBinding(binding *Binding) string {
output := fmt.Sprintf("resource \"%s\" {\n", binding.Resource)
output = fmt.Sprintf("%s roles = %s\n", output, policyRenderListOfStrings(binding.Roles))
return fmt.Sprintf("%s}\n", output)
}

func renderBindings(bindings []*Binding) string {
var output string

for i, binding := range bindings {
if i == 0 {
output = fmt.Sprintf("%s", renderBinding(binding))
} else {
output = fmt.Sprintf("%s\n\n%s", output, renderBinding(binding))
}
}

return output
}

func renderBindingsFromData(v interface{}) string {
rawBindings := v.(*schema.Set).List()

bindings := make([]*Binding, len(rawBindings))

for i, binding := range rawBindings {
rawRoles := binding.(map[string]interface{})["roles"].(*schema.Set).List()
roles := make([]string, len(rawRoles))
for j, role := range rawRoles {
roles[j] = role.(string)
}

binding := &Binding{
Resource: binding.(map[string]interface{})["resource"].(string),
Roles: roles,
}
bindings[i] = binding
}

return renderBindings(bindings)
}
12 changes: 6 additions & 6 deletions vault/resource_gcp_secret_roleset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ func TestGCPSecretRoleset(t *testing.T) {
),
},
{
ResourceName: "vault_gcp_secret_backend.test",
ResourceName: "vault_gcp_secret_roleset.test",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"credentials"},
ImportStateVerifyIgnore: []string{},
austingebauer marked this conversation as resolved.
Show resolved Hide resolved
},
{
Config: updatedConfig,
Expand Down Expand Up @@ -218,9 +218,9 @@ func testGCPSecretRoleset_attrs(backend, roleset string) resource.TestCheckFunc
return fmt.Errorf("expected %s to have %d entries in state, has %d", "binding", remoteLength, localBindingsLength)
}

flattenedBindings := gcpSecretRolesetFlattenBinding(remoteBindings).(*schema.Set)
flattenedBindings := gcpSecretFlattenBinding(remoteBindings).(*schema.Set)
for _, remoteBinding := range flattenedBindings.List() {
bindingHash := strconv.Itoa(gcpSecretRolesetBindingHash(remoteBinding))
bindingHash := strconv.Itoa(gcpSecretBindingHash(remoteBinding))

remoteResource := remoteBinding.(map[string]interface{})["resource"].(string)
localResource := instanceState.Attributes["binding."+bindingHash+".resource"]
Expand Down Expand Up @@ -336,7 +336,7 @@ resource "vault_gcp_secret_roleset" "test" {
binding["resource"] = resource
binding["roles"] = schema.NewSet(schema.HashString, roles)

return terraform, gcpSecretRolesetBindingHash(binding)
return terraform, gcpSecretBindingHash(binding)
}

func testGCPSecretRoleset_service_account_key(backend, roleset, credentials, project, role string) (string, int) {
Expand Down Expand Up @@ -369,5 +369,5 @@ resource "vault_gcp_secret_roleset" "test" {
binding["resource"] = resource
binding["roles"] = schema.NewSet(schema.HashString, roles)

return terraform, gcpSecretRolesetBindingHash(binding)
return terraform, gcpSecretBindingHash(binding)
}
Loading