From 0461be007f5e863663f2416bb661901a5f5e2557 Mon Sep 17 00:00:00 2001 From: Yong Wen Chua Date: Fri, 20 Nov 2020 11:37:37 +0800 Subject: [PATCH 01/11] Refactor some GCP functions out to a separate file --- vault/gcp.go | 96 ++++++++++++++++++++++ vault/resource_gcp_secret_roleset.go | 99 ++--------------------- vault/resource_gcp_secret_roleset_test.go | 8 +- 3 files changed, 105 insertions(+), 98 deletions(-) create mode 100644 vault/gcp.go diff --git a/vault/gcp.go b/vault/gcp.go new file mode 100644 index 000000000..b2493d22c --- /dev/null +++ b/vault/gcp.go @@ -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 Binding struct { + Resource string + Roles []string +} + +func gcpSecretFlattenBinding(v interface{}) interface{} { + if v == nil { + return v + } + + rawBindings := v.((map[string]interface{})) + transformed := schema.NewSet(gcpSecretBindingHash, []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 *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 gcpSecretRenderBindings(bindings []*Binding) 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([]*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 gcpSecretRenderBindings(bindings) +} diff --git a/vault/resource_gcp_secret_roleset.go b/vault/resource_gcp_secret_roleset.go index 9f080da58..0c2afa080 100644 --- a/vault/resource_gcp_secret_roleset.go +++ b/vault/resource_gcp_secret_roleset.go @@ -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" ) @@ -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, @@ -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": { @@ -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 }), @@ -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) } @@ -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) @@ -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 } @@ -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, "/") } @@ -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) -} diff --git a/vault/resource_gcp_secret_roleset_test.go b/vault/resource_gcp_secret_roleset_test.go index 292686007..99ff500cb 100644 --- a/vault/resource_gcp_secret_roleset_test.go +++ b/vault/resource_gcp_secret_roleset_test.go @@ -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"] @@ -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) { @@ -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) } From ac21ea347728a47baf6e520c1b2a5c84f56cf7b1 Mon Sep 17 00:00:00 2001 From: Yong Wen Chua Date: Fri, 20 Nov 2020 13:52:51 +0800 Subject: [PATCH 02/11] Add GCP Static account resource --- vault/gcp.go | 4 +- vault/provider.go | 4 + vault/resource_gcp_secret_static_account.go | 263 ++++++++++++++++++++ 3 files changed, 269 insertions(+), 2 deletions(-) create mode 100644 vault/resource_gcp_secret_static_account.go diff --git a/vault/gcp.go b/vault/gcp.go index b2493d22c..4c1523832 100644 --- a/vault/gcp.go +++ b/vault/gcp.go @@ -15,12 +15,12 @@ type Binding struct { } func gcpSecretFlattenBinding(v interface{}) interface{} { + transformed := schema.NewSet(gcpSecretBindingHash, []interface{}{}) if v == nil { - return v + return transformed } rawBindings := v.((map[string]interface{})) - transformed := schema.NewSet(gcpSecretBindingHash, []interface{}{}) for resource, roles := range rawBindings { transformed.Add(map[string]interface{}{ "resource": resource, diff --git a/vault/provider.go b/vault/provider.go index 26bfae830..f243f1e9f 100644 --- a/vault/provider.go +++ b/vault/provider.go @@ -414,6 +414,10 @@ var ( Resource: gcpSecretRolesetResource(), PathInventory: []string{"/gcp/roleset/{name}"}, }, + "vault_gcp_secret_static_account": { + Resource: gcpSecretStaticAccountResource(), + PathInventory: []string{"/gcp/static/{name}"}, + }, "vault_cert_auth_backend_role": { Resource: certAuthBackendRoleResource(), PathInventory: []string{"/auth/cert/certs/{name}"}, diff --git a/vault/resource_gcp_secret_static_account.go b/vault/resource_gcp_secret_static_account.go new file mode 100644 index 000000000..7372331fa --- /dev/null +++ b/vault/resource_gcp_secret_static_account.go @@ -0,0 +1,263 @@ +package vault + +import ( + "fmt" + "log" + "regexp" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/vault/api" +) + +var ( + gcpSecretStaticAccountBackendFromPathRegex = regexp.MustCompile("^(.+)/static/.+$") + gcpSecretStaticAccountNameFromPathRegex = regexp.MustCompile("^.+/static/(.+)$") +) + +func gcpSecretStaticAccountResource() *schema.Resource { + return &schema.Resource{ + Create: gcpSecretStaticAccountCreate, + Read: gcpSecretStaticAccountRead, + Update: gcpSecretStaticAccountUpdate, + Delete: gcpSecretStaticAccountDelete, + Exists: gcpSecretStaticAccountExists, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "backend": { + Type: schema.TypeString, + Required: true, + Description: "Path where the GCP secrets engine is mounted.", + ForceNew: true, + // standardise on no beginning or trailing slashes + StateFunc: func(v interface{}) string { + return strings.Trim(v.(string), "/") + }, + }, + "static_account": { + Type: schema.TypeString, + Required: true, + Description: "Name of the Static Account to create", + ForceNew: true, + }, + "secret_type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + Description: "Type of secret generated for this static account. Defaults to `access_token`. Accepted values: `access_token`, `service_account_key`", + }, + "service_account_email": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Email of the GCP service account.", + }, + "token_scopes": { + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Description: "List of OAuth scopes to assign to `access_token` secrets generated under this role set (`access_token` role sets only) ", + }, + "binding": { + Type: schema.TypeSet, + Optional: true, + Set: gcpSecretBindingHash, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "resource": { + Type: schema.TypeString, + Required: true, + Description: "Resource name", + }, + "roles": { + Type: schema.TypeSet, + Required: true, + Description: "List of roles to apply to the resource", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "service_account_project": { + Type: schema.TypeString, + Computed: true, + Description: "Project of the GCP Service Account managed by this static account", + }, + }, + } +} + +func gcpSecretStaticAccountCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*api.Client) + + backend := d.Get("backend").(string) + staticAccount := d.Get("static_account").(string) + + path := gcpSecretStaticAccountPath(backend, staticAccount) + + log.Printf("[DEBUG] Writing GCP Secrets backend static account %q", path) + + data := map[string]interface{}{} + gcpSecretStaticAccountUpdateFields(d, data) + d.SetId(path) + _, err := client.Logical().Write(path, data) + if err != nil { + d.SetId("") + return fmt.Errorf("Error writing GCP Secrets backend static account %q: %s", path, err) + } + log.Printf("[DEBUG] Wrote GCP Secrets backend static account %q", path) + + return gcpSecretStaticAccountRead(d, meta) +} + +func gcpSecretStaticAccountRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*api.Client) + path := d.Id() + + backend, err := gcpSecretStaticAccountBackendFromPath(path) + if err != nil { + return fmt.Errorf("invalid path %q for GCP secrets backend static account: %s", path, err) + } + + staticAccount, err := gcpSecretRoleSetdStaticAccountNameFromPath(path) + if err != nil { + return fmt.Errorf("invalid path %q for GCP Secrets backend static account: %s", path, err) + } + + log.Printf("[DEBUG] Reading GCP Secrets backend static account %q", path) + resp, err := client.Logical().Read(path) + if err != nil { + return fmt.Errorf("error reading GCP Secrets backend static account %q: %s", path, err) + } + + log.Printf("[DEBUG] Read GCP Secrets backend static account %q", path) + if resp == nil { + log.Printf("[WARN] GCP Secrets backend static account %q not found, removing from state", path) + d.SetId("") + return nil + } + + d.Set("backend", backend) + d.Set("static_account", staticAccount) + + for _, k := range []string{"secret_type", "token_scopes", "service_account_email", "service_account_project"} { + v, ok := resp.Data[k] + if ok { + if err := d.Set(k, v); err != nil { + return fmt.Errorf("error reading %s for GCP Secrets backend static account %q: %q", k, path, err) + } + } + } + + var binding interface{} + if v, ok := resp.Data["binding"]; ok && v != "" { + binding = gcpSecretFlattenBinding(resp.Data["bindings"]) + } else { + binding = gcpSecretFlattenBinding(nil) + } + if err := d.Set("binding", binding); err != nil { + return fmt.Errorf("error reading %s for GCP Secrets backend static account %q", "binding", path) + } + + return nil +} + +func gcpSecretStaticAccountUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*api.Client) + path := d.Id() + + data := map[string]interface{}{} + gcpSecretStaticAccountUpdateFields(d, data) + + log.Printf("[DEBUG] Updating GCP Secrets backend static account %q", path) + + _, err := client.Logical().Write(path, data) + if err != nil { + return fmt.Errorf("Error updating GCP Secrets backend static account %q: %s", path, err) + } + log.Printf("[DEBUG] Updated GCP Secrets backend static account %q", path) + + return gcpSecretStaticAccountRead(d, meta) +} + +func gcpSecretStaticAccountDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*api.Client) + path := d.Id() + + log.Printf("[DEBUG] Deleting GCP secrets backend static account %q", path) + _, err := client.Logical().Delete(path) + if err != nil { + return fmt.Errorf("Error deleting GCP secrets backend static account %q", path) + } + log.Printf("[DEBUG] Deleted GCP secrets backend static account %q", path) + + return nil +} + +func gcpSecretStaticAccountUpdateFields(d *schema.ResourceData, data map[string]interface{}) { + if v, ok := d.GetOk("service_account_email"); ok { + data["service_account_email"] = v.(string) + } + + if v, ok := d.GetOk("secret_type"); ok { + data["secret_type"] = v.(string) + } + + if v, ok := d.GetOk("token_scopes"); ok && d.Get("secret_type").(string) == "access_token" { + data["token_scopes"] = v.(*schema.Set).List() + } + + if v, ok := d.GetOk("binding"); ok { + bindingsHCL := gcpSecretRenderBindingsFromData(v) + log.Printf("[DEBUG] Rendered GCP Secrets backend static account bindings HCL:\n%s", bindingsHCL) + data["bindings"] = bindingsHCL + } else { + data["bindings"] = "" + } +} + +func gcpSecretStaticAccountExists(d *schema.ResourceData, meta interface{}) (bool, error) { + client := meta.(*api.Client) + path := d.Id() + log.Printf("[DEBUG] Checking if %q exists", path) + secret, err := client.Logical().Read(path) + if err != nil { + return true, fmt.Errorf("error checking if %q exists: %s", path, err) + } + log.Printf("[DEBUG] Checked if %q exists", path) + return secret != nil, nil +} + +func gcpSecretStaticAccountPath(backend, staticAccount string) string { + return strings.Trim(backend, "/") + "/static/" + strings.Trim(staticAccount, "/") +} + +func gcpSecretStaticAccountBackendFromPath(path string) (string, error) { + if !gcpSecretStaticAccountBackendFromPathRegex.MatchString(path) { + return "", fmt.Errorf("no backend found") + } + res := gcpSecretStaticAccountBackendFromPathRegex.FindStringSubmatch(path) + if len(res) != 2 { + return "", fmt.Errorf("unexpected number of matches (%d) for backend", len(res)) + } + return res[1], nil +} + +func gcpSecretRoleSetdStaticAccountNameFromPath(path string) (string, error) { + if !gcpSecretStaticAccountNameFromPathRegex.MatchString(path) { + return "", fmt.Errorf("no static account found") + } + res := gcpSecretStaticAccountNameFromPathRegex.FindStringSubmatch(path) + if len(res) != 2 { + return "", fmt.Errorf("unexpected number of matches (%d) for role", len(res)) + } + return res[1], nil +} From 0d551041733b411782dd841fc4f0e1961dd092a6 Mon Sep 17 00:00:00 2001 From: Yong Wen Chua Date: Fri, 20 Nov 2020 13:54:25 +0800 Subject: [PATCH 03/11] Rename Binding type --- vault/gcp.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vault/gcp.go b/vault/gcp.go index 4c1523832..1aa49a384 100644 --- a/vault/gcp.go +++ b/vault/gcp.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ) -type Binding struct { +type GCPBinding struct { Resource string Roles []string } @@ -53,13 +53,13 @@ func gcpSecretBindingHash(v interface{}) int { return hashcode.String(buf.String()) } -func gcpSecretRenderBinding(binding *Binding) 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 []*Binding) string { +func gcpSecretRenderBindings(bindings []*GCPBinding) string { var output string for i, binding := range bindings { @@ -76,7 +76,7 @@ func gcpSecretRenderBindings(bindings []*Binding) string { func gcpSecretRenderBindingsFromData(v interface{}) string { rawBindings := v.(*schema.Set).List() - bindings := make([]*Binding, len(rawBindings)) + bindings := make([]*GCPBinding, len(rawBindings)) for i, binding := range rawBindings { rawRoles := binding.(map[string]interface{})["roles"].(*schema.Set).List() @@ -85,7 +85,7 @@ func gcpSecretRenderBindingsFromData(v interface{}) string { roles[j] = role.(string) } - binding := &Binding{ + binding := &GCPBinding{ Resource: binding.(map[string]interface{})["resource"].(string), Roles: roles, } From 4db300da289da1b435521fb593e18d144a7a2c55 Mon Sep 17 00:00:00 2001 From: Yong Wen Chua Date: Fri, 20 Nov 2020 17:24:21 +0800 Subject: [PATCH 04/11] Add tests --- go.mod | 1 + vault/resource_gcp_secret_roleset_test.go | 4 +- vault/resource_gcp_secret_static_account.go | 4 +- ...resource_gcp_secret_static_account_test.go | 397 ++++++++++++++++++ 4 files changed, 402 insertions(+), 4 deletions(-) create mode 100644 vault/resource_gcp_secret_static_account_test.go diff --git a/go.mod b/go.mod index c74a892f5..2decf8719 100644 --- a/go.mod +++ b/go.mod @@ -18,4 +18,5 @@ require ( github.com/hashicorp/vault/sdk v0.1.14-0.20191017173300-47a54ac8bc6c 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 ) diff --git a/vault/resource_gcp_secret_roleset_test.go b/vault/resource_gcp_secret_roleset_test.go index 99ff500cb..8a4d11906 100644 --- a/vault/resource_gcp_secret_roleset_test.go +++ b/vault/resource_gcp_secret_roleset_test.go @@ -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{}, }, { Config: updatedConfig, diff --git a/vault/resource_gcp_secret_static_account.go b/vault/resource_gcp_secret_static_account.go index 7372331fa..399c53473 100644 --- a/vault/resource_gcp_secret_static_account.go +++ b/vault/resource_gcp_secret_static_account.go @@ -158,8 +158,8 @@ func gcpSecretStaticAccountRead(d *schema.ResourceData, meta interface{}) error } var binding interface{} - if v, ok := resp.Data["binding"]; ok && v != "" { - binding = gcpSecretFlattenBinding(resp.Data["bindings"]) + if v, ok := resp.Data["bindings"]; ok && v != "" { + binding = gcpSecretFlattenBinding(v) } else { binding = gcpSecretFlattenBinding(nil) } diff --git a/vault/resource_gcp_secret_static_account_test.go b/vault/resource_gcp_secret_static_account_test.go new file mode 100644 index 000000000..37717c35a --- /dev/null +++ b/vault/resource_gcp_secret_static_account_test.go @@ -0,0 +1,397 @@ +package vault + +import ( + "encoding/json" + "fmt" + "log" + "strconv" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/hashicorp/vault/api" + "golang.org/x/oauth2/google" +) + +// This test requires that you pass credentials for a user or service account having the IAM rights +// listed at https://www.vaultproject.io/docs/secrets/gcp/index.html for the project you are testing +// on. The credentials must also allow setting IAM permissions on the project being tested. +func TestGCPSecretStaticAccount(t *testing.T) { + backend := acctest.RandomWithPrefix("tf-test-gcp") + staticAccount := acctest.RandomWithPrefix("tf-test") + credentials, project := getTestGCPCreds(t) + + // We will use the provided key as the static account + conf, err := google.JWTConfigFromJSON([]byte(credentials), "https://www.googleapis.com/auth/cloud-platform") + if err != nil { + t.Fatalf("error decoding GCP Credentials: %v", err) + } + serviceAccountEmail := conf.Email + + noBindings := testGCPSecretStaticAccount_accessToken(backend, staticAccount, credentials, serviceAccountEmail, project) + + initialRole := "roles/viewer" + initialConfig, initialHash := testGCPSecretStaticAccount_accessTokenBinding(backend, staticAccount, credentials, serviceAccountEmail, project, initialRole) + + updatedRole := "roles/browser" + updatedConfig, updatedHash := testGCPSecretStaticAccount_accessTokenBinding(backend, staticAccount, credentials, serviceAccountEmail, project, updatedRole) + + keyConfig, keyHash := testGCPSecretStaticAccount_serviceAccountKey(backend, staticAccount, credentials, serviceAccountEmail, project, updatedRole) + + resource.Test(t, resource.TestCase{ + Providers: testProviders, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testGCPSecretStaticAccountDestroy, + Steps: []resource.TestStep{ + { + Config: noBindings, + Check: resource.ComposeTestCheckFunc( + testGCPSecretStaticAccount_attrs(backend, staticAccount), + resource.TestCheckResourceAttr("vault_gcp_secret_backend.test", "path", backend), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "backend", backend), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "static_account", staticAccount), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "secret_type", "access_token"), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "service_account_email", serviceAccountEmail), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "service_account_project", project), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "token_scopes.#", "1"), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "token_scopes.2400041053", "https://www.googleapis.com/auth/cloud-platform"), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "binding.#", "0"), + ), + }, + { + Config: initialConfig, + Check: resource.ComposeTestCheckFunc( + testGCPSecretStaticAccount_attrs(backend, staticAccount), + resource.TestCheckResourceAttr("vault_gcp_secret_backend.test", "path", backend), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "backend", backend), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "static_account", staticAccount), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "secret_type", "access_token"), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "service_account_email", serviceAccountEmail), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "service_account_project", project), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "token_scopes.#", "1"), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "token_scopes.2400041053", "https://www.googleapis.com/auth/cloud-platform"), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "binding.#", "1"), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", fmt.Sprintf("binding.%d.resource", initialHash), fmt.Sprintf("//cloudresourcemanager.googleapis.com/projects/%s", project)), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", fmt.Sprintf("binding.%d.roles.#", initialHash), "1"), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", fmt.Sprintf("binding.%d.roles.3993311253", initialHash), initialRole), + ), + }, + { + ResourceName: "vault_gcp_secret_static_account.test", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{}, + }, + { + Config: updatedConfig, + Check: resource.ComposeTestCheckFunc( + testGCPSecretStaticAccount_attrs(backend, staticAccount), + resource.TestCheckResourceAttr("vault_gcp_secret_backend.test", "path", backend), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "backend", backend), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "static_account", staticAccount), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "secret_type", "access_token"), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "service_account_email", serviceAccountEmail), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "service_account_project", project), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "token_scopes.#", "1"), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "token_scopes.2400041053", "https://www.googleapis.com/auth/cloud-platform"), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "binding.#", "1"), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", fmt.Sprintf("binding.%d.resource", updatedHash), fmt.Sprintf("//cloudresourcemanager.googleapis.com/projects/%s", project)), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", fmt.Sprintf("binding.%d.roles.#", updatedHash), "1"), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", fmt.Sprintf("binding.%d.roles.2133424675", updatedHash), updatedRole), + ), + }, + { + Config: keyConfig, + Check: resource.ComposeTestCheckFunc( + testGCPSecretStaticAccount_attrs(backend, staticAccount), + resource.TestCheckResourceAttr("vault_gcp_secret_backend.test", "path", backend), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "backend", backend), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "static_account", staticAccount), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "secret_type", "service_account_key"), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "service_account_email", serviceAccountEmail), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "service_account_project", project), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", "binding.#", "1"), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", fmt.Sprintf("binding.%d.resource", keyHash), fmt.Sprintf("//cloudresourcemanager.googleapis.com/projects/%s", project)), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", fmt.Sprintf("binding.%d.roles.#", keyHash), "1"), + resource.TestCheckResourceAttr("vault_gcp_secret_static_account.test", fmt.Sprintf("binding.%d.roles.2133424675", keyHash), updatedRole), + ), + }, + }, + }) +} + +func testGCPSecretStaticAccount_attrs(backend, staticAccount string) resource.TestCheckFunc { + return func(s *terraform.State) error { + resourceState := s.Modules[0].Resources["vault_gcp_secret_static_account.test"] + if resourceState == nil { + return fmt.Errorf("resource not found in state") + } + + instanceState := resourceState.Primary + if instanceState == nil { + return fmt.Errorf("resource not found in state") + } + + endpoint := instanceState.ID + + if endpoint != backend+"/static/"+staticAccount { + return fmt.Errorf("expected ID to be %q, got %q instead", backend+"/static/"+staticAccount, endpoint) + } + + client := testProvider.Meta().(*api.Client) + resp, err := client.Logical().Read(endpoint) + if err != nil { + return fmt.Errorf("%q doesn't exist", endpoint) + } + + attrs := map[string]string{ + "secret_type": "secret_type", + "service_account_project": "service_account_project", + "token_scopes": "token_scopes", + "service_account_email": "service_account_email", + } + for stateAttr, apiAttr := range attrs { + if resp.Data[apiAttr] == nil && instanceState.Attributes[stateAttr] == "" { + continue + } + var match bool + switch resp.Data[apiAttr].(type) { + case json.Number: + apiData, err := resp.Data[apiAttr].(json.Number).Int64() + if err != nil { + return fmt.Errorf("expected API field %s to be an int, was %q", apiAttr, resp.Data[apiAttr]) + } + stateData, err := strconv.ParseInt(instanceState.Attributes[stateAttr], 10, 64) + if err != nil { + return fmt.Errorf("expected state field %s to be an int, was %q", stateAttr, instanceState.Attributes[stateAttr]) + } + match = apiData == stateData + case bool: + if _, ok := resp.Data[apiAttr]; !ok && instanceState.Attributes[stateAttr] == "" { + match = true + } else { + stateData, err := strconv.ParseBool(instanceState.Attributes[stateAttr]) + if err != nil { + return fmt.Errorf("expected state field %s to be a bool, was %q", stateAttr, instanceState.Attributes[stateAttr]) + } + match = resp.Data[apiAttr] == stateData + } + case []interface{}: + apiData := resp.Data[apiAttr].([]interface{}) + length := instanceState.Attributes[stateAttr+".#"] + if length == "" { + if len(resp.Data[apiAttr].([]interface{})) != 0 { + return fmt.Errorf("expected state field %s to have %d entries, had 0", stateAttr, len(apiData)) + } + match = true + } else { + count, err := strconv.Atoi(length) + if err != nil { + return fmt.Errorf("expected %s.# to be a number, got %q", stateAttr, instanceState.Attributes[stateAttr+".#"]) + } + if count != len(apiData) { + return fmt.Errorf("expected %s to have %d entries in state, has %d", stateAttr, len(apiData), count) + } + + for i := 0; i < count; i++ { + found := false + for stateKey, stateValue := range instanceState.Attributes { + if strings.HasPrefix(stateKey, stateAttr) { + if apiData[i] == stateValue { + found = true + } + } + } + if !found { + return fmt.Errorf("Expected item %d of %s (%s in state) of %q to be in state but wasn't", i, apiAttr, stateAttr, apiData[i]) + } + } + match = true + } + default: + match = resp.Data[apiAttr] == instanceState.Attributes[stateAttr] + } + if !match { + return fmt.Errorf("expected %s (%s in state) of %q to be %q, got %q", apiAttr, stateAttr, endpoint, instanceState.Attributes[stateAttr], resp.Data[apiAttr]) + } + } + + roleHashFunction := schema.HashSchema(&schema.Schema{ + Type: schema.TypeString, + }) + + // Bindings need to be tested separately + remoteBindings := resp.Data["bindings"] // map[string]interface {} + localBindingsLengthRaw := instanceState.Attributes["binding.#"] + if localBindingsLengthRaw == "" { + return fmt.Errorf("cannot find bindings from state") + } + localBindingsLength, err := strconv.Atoi(localBindingsLengthRaw) + if err != nil { + return fmt.Errorf("expected binding.# to be a number, got %q", localBindingsLengthRaw) + } + + var remoteLength int + if remoteBindings == nil { + remoteLength = 0 + } else { + remoteLength = len(remoteBindings.(map[string]interface{})) + } + if localBindingsLength != remoteLength { + return fmt.Errorf("expected %s to have %d entries in state, has %d", "binding", remoteLength, localBindingsLength) + } + + flattenedBindings := gcpSecretFlattenBinding(remoteBindings).(*schema.Set) + for _, remoteBinding := range flattenedBindings.List() { + bindingHash := strconv.Itoa(gcpSecretBindingHash(remoteBinding)) + + remoteResource := remoteBinding.(map[string]interface{})["resource"].(string) + localResource := instanceState.Attributes["binding."+bindingHash+".resource"] + if localResource == "" { + return fmt.Errorf("expected to find binding for resource %s in state, but didn't", remoteResource) + } + if localResource != remoteResource { + return fmt.Errorf("expected to find binding for resource %s in state, but found %s instead", remoteResource, localResource) + } + + // Check Roles + remoteRoles := remoteBinding.(map[string]interface{})["roles"].(*schema.Set) + localRolesCountRaw := instanceState.Attributes["binding."+bindingHash+".roles.#"] + if localRolesCountRaw == "" { + return fmt.Errorf("cannot find role counts for the binding for resource %s", remoteResource) + } + localRolesCount, err := strconv.Atoi(localRolesCountRaw) + if err != nil { + return fmt.Errorf("expected binding.%s.roles.# to be a number, got %q", remoteResource, localRolesCountRaw) + } + if remoteRoles.Len() != localRolesCount { + return fmt.Errorf("expected %d roles for binding for resource %s but got %d instead", remoteRoles.Len(), remoteResource, localRolesCount) + } + + for _, remoteRole := range remoteRoles.List() { + roleHash := strconv.Itoa(roleHashFunction(remoteRole.(string))) + log.Printf("[DEBUG] Path to look for %s for %s", "binding."+bindingHash+".roles."+roleHash, remoteRole.(string)) + localRole := instanceState.Attributes["binding."+bindingHash+".roles."+roleHash] + if localRole == "" { + return fmt.Errorf("expected to find role %s for binding for resource %s in state, but didn't", remoteRole.(string), remoteResource) + } + + if localRole != remoteRole.(string) { + return fmt.Errorf("expected to find role %s for binding for resource %s in state, but found %s instead", remoteRole.(string), remoteResource, localRole) + } + } + } + return nil + } +} + +func testGCPSecretStaticAccountDestroy(s *terraform.State) error { + client := testProvider.Meta().(*api.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "vault_gcp_secret_static_account" { + continue + } + secret, err := client.Logical().Read(rs.Primary.ID) + if err != nil { + return fmt.Errorf("error checking for GCP Secrets StaticAccount %q: %s", rs.Primary.ID, err) + } + if secret != nil { + return fmt.Errorf("GCP Secrets StaticAccount %q still exists", rs.Primary.ID) + } + } + return nil +} + +func testGCPSecretStaticAccount_accessToken(backend, staticAccount, credentials, serviceAccountEmail, project string) string { + return fmt.Sprintf(` +resource "vault_gcp_secret_backend" "test" { + path = "%s" + credentials = < Date: Mon, 28 Jun 2021 09:10:49 +0800 Subject: [PATCH 05/11] Update --- go.sum | 31 +------------------ .../helper/resource/testing.go | 2 -- vendor/modules.txt | 1 + 3 files changed, 2 insertions(+), 32 deletions(-) diff --git a/go.sum b/go.sum index 145cba89b..a68146f0a 100644 --- a/go.sum +++ b/go.sum @@ -58,7 +58,6 @@ github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/ github.com/apple/foundationdb/bindings/go v0.0.0-20190411004307-cd5c9d91fad2/go.mod h1:OMVSB21p9+xQUIqlGizHPZfjK+SHws1ht+ZytVDoz9U= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM= github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= github.com/armon/go-metrics v0.3.3 h1:a9F4rlj7EWWrbj7BYw8J8+x+ZZkJeqzNyRk8hdPF+ro= @@ -70,7 +69,6 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.19.39/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.25.3 h1:uM16hIw9BotjZKMZlX05SN2EFtaWfi/NonPKIARiBLQ= github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.30.27 h1:9gPjZWVDSoQrBO2AvqrWObS6KAZByfEJxQoCYo4ZfK0= @@ -204,7 +202,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -223,7 +220,6 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= @@ -274,12 +270,10 @@ github.com/hashicorp/go-getter v1.4.0/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUC github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.1.0 h1:vN9wG1D6KG6YHRTWr8512cxGOVgTMEfgEdSj/hr8MPc= github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -290,7 +284,6 @@ github.com/hashicorp/go-memdb v1.0.2/go.mod h1:I6dKdmYhZqU0RJSheVEWgTNWdVQH5QvTg github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= @@ -316,8 +309,6 @@ github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjG github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2-0.20191001231223-f32f5fe8d6a8 h1:PKbxRbsOP7R3f/TpdqcgXrO69T3yd9nLoR+RMRUxSxA= -github.com/hashicorp/go-uuid v1.0.2-0.20191001231223-f32f5fe8d6a8/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= @@ -325,7 +316,6 @@ github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+d github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= @@ -375,20 +365,13 @@ github.com/hashicorp/vault-plugin-secrets-gcpkms v0.5.2-0.20190730042620-21e481a github.com/hashicorp/vault-plugin-secrets-kv v0.5.2-0.20190730042626-1ef9e711c818/go.mod h1:NVTIsqXgIu1CW+Dsnkn8AE4+QVrivR8sAzlGT7+aO58= github.com/hashicorp/vault/api v1.0.1/go.mod h1:AV/+M5VPDpB90arloVX0rVDUIHkONiwz5Uza9HRtpUE= github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= -github.com/hashicorp/vault/api v1.0.5-0.20190730042357-746c0b111519 h1:2qdbEUXjHohC+OYHtVU5lujvPAHPKYR4IMs9rsiUTk8= github.com/hashicorp/vault/api v1.0.5-0.20190730042357-746c0b111519/go.mod h1:i9PKqwFko/s/aihU1uuHGh/FaQS+Xcgvd9dvnfAvQb0= -github.com/hashicorp/vault/api v1.0.5-0.20191017173300-47a54ac8bc6c h1:H/+aHBglCDZZp2X7KUHc/gSbtxMjulGVyjgT7n1xZ6Q= -github.com/hashicorp/vault/api v1.0.5-0.20191017173300-47a54ac8bc6c/go.mod h1:8vZ3PoohxqemJEi//WSVsaMKwwXyyfP8zt9KHgBVhKU= github.com/hashicorp/vault/api v1.0.5-0.20200519221902-385fac77e20f h1:PYtnlUZzFSZxPcq7mYp5oC9N+BcJ8IKYf6/EG0GHM2Y= github.com/hashicorp/vault/api v1.0.5-0.20200519221902-385fac77e20f/go.mod h1:euTFbi2YJgwcju3imEt919lhJKF68nN1cQPq3aA+kBE= github.com/hashicorp/vault/sdk v0.1.8/go.mod h1:tHZfc6St71twLizWNHvnnbiGFo1aq0eD2jGPLtP8kAU= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= github.com/hashicorp/vault/sdk v0.1.14-0.20190729200543-e88721c7db1e/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= -github.com/hashicorp/vault/sdk v0.1.14-0.20190730042320-0dc007d98cc8 h1:fLUoZ8cI/pqlVCk09r88cVoY7ggKEl1A4e6Mujr3RvU= github.com/hashicorp/vault/sdk v0.1.14-0.20190730042320-0dc007d98cc8/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= -github.com/hashicorp/vault/sdk v0.1.14-0.20190919081434-645ac174deeb/go.mod h1:wcxXjskBz2VmyZm4MKNoLCOqsQEKkyBAUIP2YBTJL1g= -github.com/hashicorp/vault/sdk v0.1.14-0.20191017173300-47a54ac8bc6c h1:oghjhKyqPIjgJUVgA85mAyR4rXtt4B2UTFZ6FGA+kus= -github.com/hashicorp/vault/sdk v0.1.14-0.20191017173300-47a54ac8bc6c/go.mod h1:tXLVOeyErHGojiim3hA6DUSxcRisohZbpATIpln8JsE= github.com/hashicorp/vault/sdk v0.1.14-0.20200519221530-14615acda45f/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10= github.com/hashicorp/vault/sdk v0.1.14-0.20210526173046-412db2245e81 h1:RBYlAuHQhU6QjTm9leiZyga21uLRY8ZYNpHBw8o3K60= github.com/hashicorp/vault/sdk v0.1.14-0.20210526173046-412db2245e81/go.mod h1:csH/qt1ZcnoLd2Md+IKOO7XZYtAlkGyVLWW+ElnyXlQ= @@ -405,7 +388,6 @@ github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f/go.mod h1:3J2 github.com/jefferai/jsonx v1.0.0 h1:Xoz0ZbmkpBvED5W9W1B5B/zc3Oiq7oXqiW7iRV3B6EI= github.com/jefferai/jsonx v1.0.0/go.mod h1:OGmqmi2tTeI/PS+qQfBDToLHHJIy/RMp24fPo8vFvoQ= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= @@ -442,14 +424,12 @@ github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11/go.mod h1:Ah2dBMoxZEqk118as2T4u4fjfXarE0pPnMJaArZQZsI= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= @@ -480,7 +460,6 @@ github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUb github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg= github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -522,7 +501,6 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -599,6 +577,7 @@ github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -646,7 +625,6 @@ golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaE golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -685,7 +663,6 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= @@ -701,7 +678,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -734,7 +710,6 @@ golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y= @@ -746,7 +721,6 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -799,7 +773,6 @@ google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190513181449-d00d292a067c/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200310143817-43be25429f5a h1:lRlI5zu6AFy3iU/F8YWyNrAmn/tPCnhiTxfwhWb76eU= google.golang.org/genproto v0.0.0-20200310143817-43be25429f5a/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= @@ -815,7 +788,6 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= @@ -847,7 +819,6 @@ gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3M gopkg.in/ory-am/dockertest.v3 v3.3.4/go.mod h1:s9mmoLkaGeAh97qygnNj4xWkiN7e1SKekYC6CovU+ek= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.3.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing.go index 2e8597ec3..7b331d80d 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing.go @@ -1350,8 +1350,6 @@ func modulePrimaryInstanceState(s *terraform.State, ms *terraform.ModuleState, n return nil, fmt.Errorf("Not found: %s in %s", name, ms.Path) } - fmt.Println(s) - is := rs.Primary if is == nil { return nil, fmt.Errorf("No primary instance: %s in %s", name, ms.Path) diff --git a/vendor/modules.txt b/vendor/modules.txt index 99ce87508..c33d7ab2d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -431,6 +431,7 @@ golang.org/x/net/idna golang.org/x/net/internal/timeseries golang.org/x/net/trace # golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 +## explicit golang.org/x/oauth2 golang.org/x/oauth2/google golang.org/x/oauth2/internal From a251441d306306f9003a8cf59f055c7bf983514d Mon Sep 17 00:00:00 2001 From: Yong Wen Chua Date: Wed, 14 Jul 2021 10:50:16 +0800 Subject: [PATCH 06/11] Update prefix Ref https://github.com/hashicorp/vault-plugin-secrets-gcp/pull/107/commits/8fba665423e0f328670ccc579a5537998ffd8f94 --- vault/resource_gcp_secret_static_account.go | 6 +++--- vault/resource_gcp_secret_static_account_test.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/vault/resource_gcp_secret_static_account.go b/vault/resource_gcp_secret_static_account.go index 399c53473..e36462fd9 100644 --- a/vault/resource_gcp_secret_static_account.go +++ b/vault/resource_gcp_secret_static_account.go @@ -11,8 +11,8 @@ import ( ) var ( - gcpSecretStaticAccountBackendFromPathRegex = regexp.MustCompile("^(.+)/static/.+$") - gcpSecretStaticAccountNameFromPathRegex = regexp.MustCompile("^.+/static/(.+)$") + gcpSecretStaticAccountBackendFromPathRegex = regexp.MustCompile("^(.+)/static-account/.+$") + gcpSecretStaticAccountNameFromPathRegex = regexp.MustCompile("^.+/static-account/(.+)$") ) func gcpSecretStaticAccountResource() *schema.Resource { @@ -237,7 +237,7 @@ func gcpSecretStaticAccountExists(d *schema.ResourceData, meta interface{}) (boo } func gcpSecretStaticAccountPath(backend, staticAccount string) string { - return strings.Trim(backend, "/") + "/static/" + strings.Trim(staticAccount, "/") + return strings.Trim(backend, "/") + "/static-account/" + strings.Trim(staticAccount, "/") } func gcpSecretStaticAccountBackendFromPath(path string) (string, error) { diff --git a/vault/resource_gcp_secret_static_account_test.go b/vault/resource_gcp_secret_static_account_test.go index 37717c35a..f7a35bb24 100644 --- a/vault/resource_gcp_secret_static_account_test.go +++ b/vault/resource_gcp_secret_static_account_test.go @@ -137,8 +137,8 @@ func testGCPSecretStaticAccount_attrs(backend, staticAccount string) resource.Te endpoint := instanceState.ID - if endpoint != backend+"/static/"+staticAccount { - return fmt.Errorf("expected ID to be %q, got %q instead", backend+"/static/"+staticAccount, endpoint) + if endpoint != backend+"/static-account/"+staticAccount { + return fmt.Errorf("expected ID to be %q, got %q instead", backend+"/static-account/"+staticAccount, endpoint) } client := testProvider.Meta().(*api.Client) From f0359774d17a59349637be198c79d2d4da9ceb59 Mon Sep 17 00:00:00 2001 From: Yong Wen Chua Date: Wed, 14 Jul 2021 12:35:23 +0800 Subject: [PATCH 07/11] Add docs --- .../docs/r/gcp_secret_static_account.html.md | 81 +++++++++++++++++++ website/vault.erb | 4 + 2 files changed, 85 insertions(+) create mode 100644 website/docs/r/gcp_secret_static_account.html.md diff --git a/website/docs/r/gcp_secret_static_account.html.md b/website/docs/r/gcp_secret_static_account.html.md new file mode 100644 index 000000000..88d62a347 --- /dev/null +++ b/website/docs/r/gcp_secret_static_account.html.md @@ -0,0 +1,81 @@ +--- +layout: "vault" +page_title: "Vault: vault_gcp_secret_static_account resource" +sidebar_current: "docs-vault-resource-gcp-secret-statoc-account" +description: |- + Creates a Static Account for the GCP Secret Backend for Vault. +--- + +# vault\_gcp\_secret\_static\_account + +Creates a Static Account in the [GCP Secrets Engine](https://www.vaultproject.io/docs/secrets/gcp/index.html) for Vault. + +Each [static account](https://www.vaultproject.io/docs/secrets/gcp/index.html#static-accounts) is tied to a separately managed +Service Account, and can have one or more [bindings](https://www.vaultproject.io/docs/secrets/gcp/index.html#bindings) associated with it. + +## Example Usage + +```hcl +resource "google_service_account" "this" { + account_id = "my-awesome-account" +} + +resource "vault_gcp_secret_backend" "gcp" { + path = "gcp" + credentials = "${file("credentials.json")}" +} + +resource "vault_gcp_secret_static_account" "static_account" { + backend = vault_gcp_secret_backend.gcp.path + static_account = "project_viewer" + secret_type = "access_token" + token_scopes = ["https://www.googleapis.com/auth/cloud-platform"] + + service_account_email = google_service_account.this.email + + # Optional + binding { + resource = "//cloudresourcemanager.googleapis.com/projects/${google_service_account.this.project}" + + roles = [ + "roles/viewer", + ] + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `backend` - (Required, Forces new resource) Path where the GCP Secrets Engine is mounted + +* `static_account` - (Required, Forces new resource) Name of the Static Account to create + +* `service_account_email` - (Required, Forces new resource) Email of the GCP service account to manage. + +* `secret_type` - (Optional, Forces new resource) Type of secret generated for this role set. Accepted values: `access_token`, `service_account_key`. Defaults to `access_token`. + +* `token_scopes` - (Optional, Required for `secret_type = "access_token"`) List of OAuth scopes to assign to `access_token` secrets generated under this role set (`access_token` role sets only). + +* `binding` - (Optional) Bindings to create for this static account. This can be specified multiple times for multiple bindings. Structure is documented below. + +The `binding` block supports: + +* `resource` - (Required) Resource or resource path for which IAM policy information will be bound. The resource path may be specified in a few different [formats](https://www.vaultproject.io/docs/secrets/gcp/index.html#bindings). + +* `roles` - (Required) List of [GCP IAM roles](https://cloud.google.com/iam/docs/understanding-roles) for the resource. + +## Attributes Reference + +In addition to the fields above, the following attributes are also exposed: + +* `service_account_project` - Project the service account belongs to. + +## Import + +A static account can be imported using its Vault Path. For example, referencing the example above, + +``` +$ terraform import vault_gcp_secret_static_account.static_account gcp/static-account/project_viewer +``` diff --git a/website/vault.erb b/website/vault.erb index 3326095c4..192e0bb1c 100644 --- a/website/vault.erb +++ b/website/vault.erb @@ -205,6 +205,10 @@ vault_gcp_secret_roleset + > + vault_gcp_secret_static_account + + > vault_generic_endpoint From a7244b021ea44023fc2a23f8317ba86fcbb04063 Mon Sep 17 00:00:00 2001 From: Yong Wen Chua Date: Thu, 29 Jul 2021 10:07:41 +0800 Subject: [PATCH 08/11] Apply suggestions from code review Co-authored-by: Austin Gebauer <34121980+austingebauer@users.noreply.github.com> --- vault/provider.go | 2 +- vault/resource_gcp_secret_static_account.go | 8 ++++---- website/docs/r/gcp_secret_static_account.html.md | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/vault/provider.go b/vault/provider.go index c108c4931..2825b2e95 100644 --- a/vault/provider.go +++ b/vault/provider.go @@ -429,7 +429,7 @@ var ( }, "vault_gcp_secret_static_account": { Resource: gcpSecretStaticAccountResource(), - PathInventory: []string{"/gcp/static/{name}"}, + PathInventory: []string{"/gcp/static-account/{name}"}, }, "vault_cert_auth_backend_role": { Resource: certAuthBackendRoleResource(), diff --git a/vault/resource_gcp_secret_static_account.go b/vault/resource_gcp_secret_static_account.go index e36462fd9..928345177 100644 --- a/vault/resource_gcp_secret_static_account.go +++ b/vault/resource_gcp_secret_static_account.go @@ -62,7 +62,7 @@ func gcpSecretStaticAccountResource() *schema.Resource { Type: schema.TypeString, }, Optional: true, - Description: "List of OAuth scopes to assign to `access_token` secrets generated under this role set (`access_token` role sets only) ", + Description: "List of OAuth scopes to assign to `access_token` secrets generated under this static account (`access_token` static accounts only) ", }, "binding": { Type: schema.TypeSet, @@ -111,7 +111,7 @@ func gcpSecretStaticAccountCreate(d *schema.ResourceData, meta interface{}) erro _, err := client.Logical().Write(path, data) if err != nil { d.SetId("") - return fmt.Errorf("Error writing GCP Secrets backend static account %q: %s", path, err) + return fmt.Errorf("error writing GCP Secrets backend static account %q: %s", path, err) } log.Printf("[DEBUG] Wrote GCP Secrets backend static account %q", path) @@ -181,7 +181,7 @@ func gcpSecretStaticAccountUpdate(d *schema.ResourceData, meta interface{}) erro _, err := client.Logical().Write(path, data) if err != nil { - return fmt.Errorf("Error updating GCP Secrets backend static account %q: %s", path, err) + return fmt.Errorf("error updating GCP Secrets backend static account %q: %s", path, err) } log.Printf("[DEBUG] Updated GCP Secrets backend static account %q", path) @@ -195,7 +195,7 @@ func gcpSecretStaticAccountDelete(d *schema.ResourceData, meta interface{}) erro log.Printf("[DEBUG] Deleting GCP secrets backend static account %q", path) _, err := client.Logical().Delete(path) if err != nil { - return fmt.Errorf("Error deleting GCP secrets backend static account %q", path) + return fmt.Errorf("error deleting GCP secrets backend static account %q", path) } log.Printf("[DEBUG] Deleted GCP secrets backend static account %q", path) diff --git a/website/docs/r/gcp_secret_static_account.html.md b/website/docs/r/gcp_secret_static_account.html.md index 88d62a347..85d8185b4 100644 --- a/website/docs/r/gcp_secret_static_account.html.md +++ b/website/docs/r/gcp_secret_static_account.html.md @@ -1,7 +1,7 @@ --- layout: "vault" page_title: "Vault: vault_gcp_secret_static_account resource" -sidebar_current: "docs-vault-resource-gcp-secret-statoc-account" +sidebar_current: "docs-vault-resource-gcp-secret-static-account" description: |- Creates a Static Account for the GCP Secret Backend for Vault. --- @@ -54,9 +54,9 @@ The following arguments are supported: * `service_account_email` - (Required, Forces new resource) Email of the GCP service account to manage. -* `secret_type` - (Optional, Forces new resource) Type of secret generated for this role set. Accepted values: `access_token`, `service_account_key`. Defaults to `access_token`. +* `secret_type` - (Optional, Forces new resource) Type of secret generated for this static account. Accepted values: `access_token`, `service_account_key`. Defaults to `access_token`. -* `token_scopes` - (Optional, Required for `secret_type = "access_token"`) List of OAuth scopes to assign to `access_token` secrets generated under this role set (`access_token` role sets only). +* `token_scopes` - (Optional, Required for `secret_type = "access_token"`) List of OAuth scopes to assign to `access_token` secrets generated under this static account (`access_token` static accounts only). * `binding` - (Optional) Bindings to create for this static account. This can be specified multiple times for multiple bindings. Structure is documented below. From 661a3ebd150c2cd66cc22ccaf087bfe371e041f8 Mon Sep 17 00:00:00 2001 From: Yong Wen Chua Date: Thu, 29 Jul 2021 11:18:18 +0800 Subject: [PATCH 09/11] Rename function --- vault/resource_gcp_secret_static_account.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vault/resource_gcp_secret_static_account.go b/vault/resource_gcp_secret_static_account.go index 928345177..efdaecfe0 100644 --- a/vault/resource_gcp_secret_static_account.go +++ b/vault/resource_gcp_secret_static_account.go @@ -127,7 +127,7 @@ func gcpSecretStaticAccountRead(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("invalid path %q for GCP secrets backend static account: %s", path, err) } - staticAccount, err := gcpSecretRoleSetdStaticAccountNameFromPath(path) + staticAccount, err := gcpSecretStaticAccountNameFromPath(path) if err != nil { return fmt.Errorf("invalid path %q for GCP Secrets backend static account: %s", path, err) } @@ -251,7 +251,7 @@ func gcpSecretStaticAccountBackendFromPath(path string) (string, error) { return res[1], nil } -func gcpSecretRoleSetdStaticAccountNameFromPath(path string) (string, error) { +func gcpSecretStaticAccountNameFromPath(path string) (string, error) { if !gcpSecretStaticAccountNameFromPathRegex.MatchString(path) { return "", fmt.Errorf("no static account found") } From 960970b51db908486328572e903cd524442cbeb7 Mon Sep 17 00:00:00 2001 From: Yong Wen Chua Date: Fri, 30 Jul 2021 09:47:37 +0800 Subject: [PATCH 10/11] Update vault/resource_gcp_secret_static_account.go Co-authored-by: Ben Ash <32777270+benashz@users.noreply.github.com> --- vault/resource_gcp_secret_static_account.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/vault/resource_gcp_secret_static_account.go b/vault/resource_gcp_secret_static_account.go index efdaecfe0..1cf5d14b4 100644 --- a/vault/resource_gcp_secret_static_account.go +++ b/vault/resource_gcp_secret_static_account.go @@ -145,8 +145,12 @@ func gcpSecretStaticAccountRead(d *schema.ResourceData, meta interface{}) error return nil } - d.Set("backend", backend) - d.Set("static_account", staticAccount) + if err := d.Set("backend", backend); err != nil { + return err + } + if err := d.Set("static_account", staticAccount); err != nil { + return err + } for _, k := range []string{"secret_type", "token_scopes", "service_account_email", "service_account_project"} { v, ok := resp.Data[k] From 2ebbfc586a1d5b3ee61dfb893a69ac7d910e7739 Mon Sep 17 00:00:00 2001 From: Yong Wen Chua Date: Fri, 30 Jul 2021 10:57:57 +0800 Subject: [PATCH 11/11] Add some comments --- vault/gcp.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vault/gcp.go b/vault/gcp.go index 1aa49a384..cbb79c99d 100644 --- a/vault/gcp.go +++ b/vault/gcp.go @@ -9,6 +9,9 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ) +/// GCPBinding is used to generate the HCL binding format that GCP Secret Engine Requires +/// `Resource` is the self-link of a GCP resource +/// Roles is a list of IAM roles to be assigned to an entity for that resource. type GCPBinding struct { Resource string Roles []string