-
Notifications
You must be signed in to change notification settings - Fork 549
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Phase 2] Complete the provider configuration setup (#2090)
* setup framework provider schema * handle defaults in configure * WIP: handle auth login methods * [Phase 2] Include Auth Login blocks in multiplexed Provider configuration (#2097) make updates to all auth logins and fix build run go mod tidy register all auth logins to test build add all auth login blocks and match schema implementations set default for azure scope address todos - remove commented code; neither are read from env vars - add validators * remove set_namespace_from_token field * add custom path validator * configure namespace for vault client * review comments * add muxed provider acc tests * use consts, set required fields and update tests * add tests for multi envs and fix bug * fix bug with overriding config val * add custom URI validator for OIDC auth * fix a few default mount types * add comments and migration state example acc test * remove dupe default entries; add validators * remove client_auth docs * add kerbos token and file path validators * don't use double pointer and remove redundant return --------- Co-authored-by: vinay-gopalan <86625824+vinay-gopalan@users.noreply.github.com>
- Loading branch information
1 parent
a0ba33f
commit bbf6580
Showing
53 changed files
with
2,287 additions
and
145 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package base | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/resource/schema" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" | ||
"github.com/hashicorp/terraform-plugin-framework/schema/validator" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
"github.com/hashicorp/terraform-provider-vault/internal/consts" | ||
"github.com/hashicorp/terraform-provider-vault/internal/framework/validators" | ||
) | ||
|
||
// BaseModel describes common fields for all of the Terraform resource data models | ||
type BaseModel struct { | ||
Namespace types.String `tfsdk:"namespace"` | ||
} | ||
|
||
func baseSchema() map[string]schema.Attribute { | ||
return map[string]schema.Attribute{ | ||
consts.FieldNamespace: schema.StringAttribute{ | ||
Optional: true, | ||
PlanModifiers: []planmodifier.String{ | ||
stringplanmodifier.RequiresReplace(), | ||
}, | ||
MarkdownDescription: "Target namespace. (requires Enterprise)", | ||
Validators: []validator.String{ | ||
validators.PathValidator(), | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func MustAddBaseSchema(s *schema.Schema) { | ||
for k, v := range baseSchema() { | ||
if _, ok := s.Attributes[k]; ok { | ||
panic(fmt.Sprintf("cannot add schema field %q, already exists in the Schema map", k)) | ||
} | ||
|
||
s.Attributes[k] = v | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package client | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
|
||
"github.com/hashicorp/terraform-plugin-log/tflog" | ||
"github.com/hashicorp/terraform-provider-vault/internal/consts" | ||
"github.com/hashicorp/terraform-provider-vault/internal/provider" | ||
"github.com/hashicorp/vault/api" | ||
) | ||
|
||
func GetClient(ctx context.Context, meta interface{}, namespace string) (*api.Client, error) { | ||
var p *provider.ProviderMeta | ||
|
||
switch v := meta.(type) { | ||
case *provider.ProviderMeta: | ||
p = v | ||
default: | ||
return nil, fmt.Errorf("meta argument must be a %T, not %T", p, meta) | ||
} | ||
|
||
ns := namespace | ||
if namespace == "" { | ||
// in order to import namespaced resources the user must provide | ||
// the namespace from an environment variable. | ||
ns = os.Getenv(consts.EnvVarVaultNamespaceImport) | ||
if ns != "" { | ||
tflog.Debug(ctx, fmt.Sprintf("Value for %q set from environment", consts.FieldNamespace)) | ||
} | ||
} | ||
|
||
if ns != "" { | ||
return p.GetNSClient(ns) | ||
} | ||
|
||
return p.GetClient() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Terraform Plugin Framework Validators | ||
|
||
This package contains custom Terraform Plugin Framework [validators](https://developer.hashicorp.com/terraform/plugin/framework/validation). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package validators | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/schema/validator" | ||
"github.com/mitchellh/go-homedir" | ||
) | ||
|
||
var _ validator.String = fileExists{} | ||
|
||
// fileExists validates that a given token is a valid initialization token | ||
type fileExists struct{} | ||
|
||
// Description describes the validation in plain text formatting. | ||
func (v fileExists) Description(_ context.Context) string { | ||
return "value must be a valid path to an existing file" | ||
} | ||
|
||
// MarkdownDescription describes the validation in Markdown formatting. | ||
func (v fileExists) MarkdownDescription(ctx context.Context) string { | ||
return v.Description(ctx) | ||
} | ||
|
||
// ValidateString performs the validation. | ||
func (v fileExists) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) { | ||
if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() { | ||
return | ||
} | ||
|
||
value := request.ConfigValue.ValueString() | ||
if value == "" { | ||
response.Diagnostics.AddError("invalid file", "value cannot be empty") | ||
return | ||
} | ||
|
||
filename, err := homedir.Expand(value) | ||
if err != nil { | ||
response.Diagnostics.AddError("invalid file", err.Error()) | ||
return | ||
} | ||
|
||
st, err := os.Stat(filename) | ||
if err != nil { | ||
response.Diagnostics.AddError("invalid file", fmt.Sprintf("failed to stat path %q, err=%s", filename, err)) | ||
return | ||
} | ||
|
||
if st.IsDir() { | ||
response.Diagnostics.AddError("invalid file", fmt.Sprintf("path %q is not a file", filename)) | ||
return | ||
} | ||
} | ||
|
||
func FileExistsValidator() validator.String { | ||
return fileExists{} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package validators | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/diag" | ||
"github.com/hashicorp/terraform-plugin-framework/schema/validator" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
) | ||
|
||
const testFilePath = "./testdata/fake_account.json" | ||
|
||
func TestFrameworkProvider_FileExistsValidator(t *testing.T) { | ||
cases := map[string]struct { | ||
configValue func(t *testing.T) types.String | ||
expectedErrorCount int | ||
}{ | ||
"file-is-valid": { | ||
configValue: func(t *testing.T) types.String { | ||
return types.StringValue(testFilePath) // Path to a test fixture | ||
}, | ||
}, | ||
"non-existant-file-is-not-valid": { | ||
configValue: func(t *testing.T) types.String { | ||
return types.StringValue("./this/path/doesnt/exist.json") // Doesn't exist | ||
}, | ||
expectedErrorCount: 1, | ||
}, | ||
"empty-string-is-not-valid": { | ||
configValue: func(t *testing.T) types.String { | ||
return types.StringValue("") | ||
}, | ||
expectedErrorCount: 1, | ||
}, | ||
"unconfigured-is-valid": { | ||
configValue: func(t *testing.T) types.String { | ||
return types.StringNull() | ||
}, | ||
}, | ||
} | ||
|
||
for tn, tc := range cases { | ||
t.Run(tn, func(t *testing.T) { | ||
// Arrange | ||
req := validator.StringRequest{ | ||
ConfigValue: tc.configValue(t), | ||
} | ||
|
||
resp := validator.StringResponse{ | ||
Diagnostics: diag.Diagnostics{}, | ||
} | ||
|
||
f := FileExistsValidator() | ||
|
||
// Act | ||
f.ValidateString(context.Background(), req, &resp) | ||
|
||
// Assert | ||
if resp.Diagnostics.ErrorsCount() != tc.expectedErrorCount { | ||
t.Errorf("Expected %d errors, got %d", tc.expectedErrorCount, resp.Diagnostics.ErrorsCount()) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package validators | ||
|
||
import ( | ||
"context" | ||
"os" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/schema/validator" | ||
googleoauth "golang.org/x/oauth2/google" | ||
) | ||
|
||
// Credentials Validator | ||
var _ validator.String = credentialsValidator{} | ||
|
||
// credentialsValidator validates that a string Attribute's is valid JSON credentials. | ||
type credentialsValidator struct{} | ||
|
||
// Description describes the validation in plain text formatting. | ||
func (v credentialsValidator) Description(_ context.Context) string { | ||
return "value must be a path to valid JSON credentials or valid, raw, JSON credentials" | ||
} | ||
|
||
// MarkdownDescription describes the validation in Markdown formatting. | ||
func (v credentialsValidator) MarkdownDescription(ctx context.Context) string { | ||
return v.Description(ctx) | ||
} | ||
|
||
// ValidateString performs the validation. | ||
func (v credentialsValidator) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) { | ||
if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() { | ||
return | ||
} | ||
|
||
value := request.ConfigValue.ValueString() | ||
|
||
// if this is a path and we can stat it, assume it's ok | ||
if _, err := os.Stat(value); err == nil { | ||
return | ||
} | ||
if _, err := googleoauth.CredentialsFromJSON(context.Background(), []byte(value)); err != nil { | ||
response.Diagnostics.AddError("JSON credentials are not valid", err.Error()) | ||
} | ||
} | ||
|
||
func GCPCredentialsValidator() validator.String { | ||
return credentialsValidator{} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package validators | ||
|
||
import ( | ||
"context" | ||
"io/ioutil" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/diag" | ||
"github.com/hashicorp/terraform-plugin-framework/schema/validator" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
) | ||
|
||
const testFakeCredentialsPath = "./testdata/fake_account.json" | ||
|
||
func TestFrameworkProvider_CredentialsValidator(t *testing.T) { | ||
cases := map[string]struct { | ||
configValue func(t *testing.T) types.String | ||
expectedErrorCount int | ||
}{ | ||
"configuring credentials as a path to a credentials JSON file is valid": { | ||
configValue: func(t *testing.T) types.String { | ||
return types.StringValue(testFakeCredentialsPath) // Path to a test fixture | ||
}, | ||
}, | ||
"configuring credentials as a path to a non-existant file is NOT valid": { | ||
configValue: func(t *testing.T) types.String { | ||
return types.StringValue("./this/path/doesnt/exist.json") // Doesn't exist | ||
}, | ||
expectedErrorCount: 1, | ||
}, | ||
"configuring credentials as a credentials JSON string is valid": { | ||
configValue: func(t *testing.T) types.String { | ||
contents, err := ioutil.ReadFile(testFakeCredentialsPath) | ||
if err != nil { | ||
t.Fatalf("Unexpected error: %s", err) | ||
} | ||
stringContents := string(contents) | ||
return types.StringValue(stringContents) | ||
}, | ||
}, | ||
"configuring credentials as an empty string is not valid": { | ||
configValue: func(t *testing.T) types.String { | ||
return types.StringValue("") | ||
}, | ||
expectedErrorCount: 1, | ||
}, | ||
"leaving credentials unconfigured is valid": { | ||
configValue: func(t *testing.T) types.String { | ||
return types.StringNull() | ||
}, | ||
}, | ||
} | ||
|
||
for tn, tc := range cases { | ||
t.Run(tn, func(t *testing.T) { | ||
// Arrange | ||
req := validator.StringRequest{ | ||
ConfigValue: tc.configValue(t), | ||
} | ||
|
||
resp := validator.StringResponse{ | ||
Diagnostics: diag.Diagnostics{}, | ||
} | ||
|
||
cv := GCPCredentialsValidator() | ||
|
||
// Act | ||
cv.ValidateString(context.Background(), req, &resp) | ||
|
||
// Assert | ||
if resp.Diagnostics.ErrorsCount() != tc.expectedErrorCount { | ||
t.Errorf("Expected %d errors, got %d", tc.expectedErrorCount, resp.Diagnostics.ErrorsCount()) | ||
} | ||
}) | ||
} | ||
} |
Oops, something went wrong.