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

feat: add "avoid jaas" validator to existing resources #569

Merged
merged 2 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion internal/provider/resource_access_generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ type genericJAASAccessModel struct {
// ConfigValidators sets validators for the resource.
func (r *genericJAASAccessResource) ConfigValidators(ctx context.Context) []resource.ConfigValidator {
return []resource.ConfigValidator{
RequiresJAASValidator{Client: r.client},
NewRequiresJAASValidator(r.client),
kian99 marked this conversation as resolved.
Show resolved Hide resolved
resourcevalidator.AtLeastOneOf(
path.MatchRoot("users"),
path.MatchRoot("groups"),
Expand Down
1 change: 1 addition & 0 deletions internal/provider/resource_access_jaas_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
// Ensure provider defined types fully satisfy framework interfaces.
var _ resource.Resource = &jaasAccessModelResource{}
var _ resource.ResourceWithConfigure = &jaasAccessModelResource{}
var _ resource.ResourceWithConfigValidators = &jaasAccessModelResource{}

// NewJAASAccessModelResource returns a new resource for JAAS model access.
func NewJAASAccessModelResource() resource.Resource {
Expand Down
8 changes: 8 additions & 0 deletions internal/provider/resource_access_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
var _ resource.Resource = &accessModelResource{}
var _ resource.ResourceWithConfigure = &accessModelResource{}
var _ resource.ResourceWithImportState = &accessModelResource{}
var _ resource.ResourceWithConfigValidators = &accessModelResource{}

func NewAccessModelResource() resource.Resource {
return &accessModelResource{}
Expand All @@ -52,6 +53,13 @@ func (a *accessModelResource) Metadata(_ context.Context, req resource.MetadataR
resp.TypeName = req.ProviderTypeName + "_access_model"
}

// ConfigValidators sets validators for the resource.
func (r *accessModelResource) ConfigValidators(ctx context.Context) []resource.ConfigValidator {
kian99 marked this conversation as resolved.
Show resolved Hide resolved
return []resource.ConfigValidator{
NewAvoidJAASValidator(r.client, "juju_jaas_access_model"),
}
}

func (a *accessModelResource) Schema(_ context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "A resource that represent a Juju Access Model.",
Expand Down
24 changes: 24 additions & 0 deletions internal/provider/resource_access_model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,30 @@ func TestAcc_ResourceAccessModel_UpgradeProvider(t *testing.T) {
})
}

func TestAcc_ResourceAccessModel_ErrorWhenUsedWithJAAS(t *testing.T) {
OnlyTestAgainstJAAS(t)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: frameworkProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccResourceAccessModelFixedUser(),
ExpectError: regexp.MustCompile("This resource is not supported with JAAS"),
},
},
})
}

func testAccResourceAccessModelFixedUser() string {
return `
resource "juju_access_model" "test" {
access = "write"
model = "foo"
users = ["bob"]
}`
}

func testAccResourceAccessModel(userName, userPassword, modelName, access string) string {
return fmt.Sprintf(`
resource "juju_user" "test-user" {
Expand Down
9 changes: 9 additions & 0 deletions internal/provider/resource_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
var _ resource.Resource = &userResource{}
var _ resource.ResourceWithConfigure = &userResource{}
var _ resource.ResourceWithImportState = &userResource{}
var _ resource.ResourceWithConfigValidators = &userResource{}

func NewUserResource() resource.Resource {
return &userResource{}
Expand Down Expand Up @@ -51,6 +52,14 @@ func (r *userResource) Metadata(ctx context.Context, req resource.MetadataReques
resp.TypeName = req.ProviderTypeName + "_user"
}

// ConfigValidators sets validators for the resource.
func (r *userResource) ConfigValidators(ctx context.Context) []resource.ConfigValidator {
kian99 marked this conversation as resolved.
Show resolved Hide resolved
return []resource.ConfigValidator{
// There is no JAAS object that replaces the user resource since JAAS users come from an external identity provider.
NewAvoidJAASValidator(r.client, ""),
}
}

func (r *userResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
// The User resource maps to a juju user that is operated via
// `juju add-user`, `juju remove-user`
Expand Down
19 changes: 18 additions & 1 deletion internal/provider/resource_user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package provider

import (
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
Expand All @@ -26,7 +27,6 @@ func TestAcc_ResourceUser(t *testing.T) {
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", userName),
),
PreConfig: func() { testAccPreCheck(t) },
},
{
Destroy: true,
Expand Down Expand Up @@ -78,3 +78,20 @@ func TestAcc_ResourceUser_UpgradeProvider(t *testing.T) {
},
})
}

func TestAcc_ResourceUser_ErrorWhenUsedWithJAAS(t *testing.T) {
OnlyTestAgainstJAAS(t)
userName := acctest.RandomWithPrefix("tfuser")
userPassword := acctest.RandomWithPrefix("tf-test-user")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: frameworkProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccResourceUser(userName, userPassword),
ExpectError: regexp.MustCompile("This resource is not supported with JAAS"),
},
},
})
}
23 changes: 17 additions & 6 deletions internal/provider/validator_avoid_jaas.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,19 @@ var _ resource.ConfigValidator = &AvoidJAASValidator{}
// AvoidJAASValidator enforces that the resource is not used with JAAS.
// Useful to direct users to more capable resources.
type AvoidJAASValidator struct {
Client *juju.Client
PreferredObject string
client *juju.Client
preferredObject string
}

// NewAvoidJAASValidator creates a new validator that can be used with resources or
// data sources to enforce that the resource cannot be used with JAAS.
// Provide a non-empty string for preferredObject to point users to an alternate object in the error messsage.
// If preferredObject is empty, no hint on an alternate object will be offered in the error message.
func NewAvoidJAASValidator(client *juju.Client, preferredObject string) AvoidJAASValidator {
return AvoidJAASValidator{
client: client,
preferredObject: preferredObject,
}
}

// Description returns a plain text description of the validator's behavior, suitable for a practitioner to understand its impact.
Expand Down Expand Up @@ -49,15 +60,15 @@ func (v AvoidJAASValidator) validate() diag.Diagnostics {

// Return without error if a nil client is detected.
// This is possible since validation is called at various points throughout resource creation.
if v.Client != nil && v.Client.IsJAAS() {
if v.client != nil && v.client.IsJAAS() {
hint := ""
if v.PreferredObject != "" {
hint = "Try the " + v.PreferredObject + " resource instead."
if v.preferredObject != "" {
hint = "Try the " + v.preferredObject + " resource instead."
}
diags.AddError("Invalid use of resource with JAAS.",
"This resource is not supported with JAAS. "+
hint+
"JAAS offers additional enterprise features through the use of dedicated resources. "+
`JAAS offers additional enterprise features through the use of dedicated resources (those with "jaas" in the name). `+
"See https://jaas.ai/ for more details.")
}
return diags
Expand Down
12 changes: 10 additions & 2 deletions internal/provider/validator_require_jaas.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@ var _ resource.ConfigValidator = &RequiresJAASValidator{}

// RequiresJAASValidator enforces that the resource can only be used with JAAS.
type RequiresJAASValidator struct {
Client *juju.Client
client *juju.Client
}

// NewRequiresJAASValidator returns a new validator that enforces a resource can
// only be created against JAAS.
func NewRequiresJAASValidator(client *juju.Client) RequiresJAASValidator {
kian99 marked this conversation as resolved.
Show resolved Hide resolved
return RequiresJAASValidator{
client: client,
}
}

// Description returns a plain text description of the validator's behavior, suitable for a practitioner to understand its impact.
Expand Down Expand Up @@ -47,7 +55,7 @@ func (v RequiresJAASValidator) validate() diag.Diagnostics {

// Return without error if a nil client is detected.
// This is possible since validation is called at various points throughout resource creation.
if v.Client != nil && !v.Client.IsJAAS() {
if v.client != nil && !v.client.IsJAAS() {
diags.AddError("Attempted use of resource without JAAS.",
"This resource can only be used with JAAS, which offers additional enterprise features - see https://jaas.ai/ for more details.")
}
Expand Down