Skip to content

Commit

Permalink
feat: implement crud for the generic access resource
Browse files Browse the repository at this point in the history
  • Loading branch information
kian99 committed Aug 30, 2024
1 parent 43b9927 commit d0a93a9
Show file tree
Hide file tree
Showing 10 changed files with 765 additions and 43 deletions.
10 changes: 8 additions & 2 deletions internal/juju/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"sync"
"time"

jaasApi "github.com/canonical/jimm-go-sdk/v3/api"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/juju/errors"
"github.com/juju/juju/api"
Expand Down Expand Up @@ -125,13 +126,18 @@ var isJAAS bool
// IsJAAS uses a synchronisation object to only perform the check once and return the same result.
func (sc *sharedClient) IsJAAS(defaultVal bool) bool {
checkJAASOnce.Do(func() {
isJAAS = defaultVal
conn, err := sc.GetConnection(nil)
if err != nil {
isJAAS = defaultVal
return
}
defer conn.Close()
isJAAS = conn.BestFacadeVersion("JIMM") != 0
jc := jaasApi.NewClient(conn)
_, err = jc.ListControllers()
if err == nil {
isJAAS = true
return
}
})
return isJAAS
}
Expand Down
22 changes: 19 additions & 3 deletions internal/juju/jaas.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@ func toAPITuple(tuple JaasTuple) params.RelationshipTuple {
}
}

func toJaasTuples(tuples []params.RelationshipTuple) []JaasTuple {
out := make([]JaasTuple, 0, len(tuples))
for _, tuple := range tuples {
out = append(out, toJaasTuple(tuple))
}
return out
}

func toJaasTuple(tuple params.RelationshipTuple) JaasTuple {
return JaasTuple{
Object: tuple.Object,
Relation: tuple.Relation,
Target: tuple.TargetObject,
}
}

// AddRelations attempts to create the provided slice of relationship tuples.
// An empty slice of tuples will return an error.
func (jc *jaasClient) AddRelations(tuples []JaasTuple) error {
Expand Down Expand Up @@ -90,7 +106,7 @@ func (jc *jaasClient) DeleteRelations(tuples []JaasTuple) error {

// ReadRelations attempts to read relations that match the criteria defined by `tuple`.
// An nil tuple pointer is invalid and will return an error.
func (jc *jaasClient) ReadRelations(ctx context.Context, tuple *JaasTuple) ([]params.RelationshipTuple, error) {
func (jc *jaasClient) ReadRelations(ctx context.Context, tuple *JaasTuple) ([]JaasTuple, error) {
if tuple == nil {
return nil, errors.New("read relation tuple is nil")
}
Expand All @@ -102,7 +118,7 @@ func (jc *jaasClient) ReadRelations(ctx context.Context, tuple *JaasTuple) ([]pa
defer func() { _ = conn.Close() }()

client := jc.getJaasApiClient(conn)
relations := make([]params.RelationshipTuple, 0)
relations := make([]JaasTuple, 0)
req := &params.ListRelationshipTuplesRequest{Tuple: toAPITuple(*tuple)}
for {
resp, err := client.ListRelationshipTuples(req)
Expand All @@ -114,7 +130,7 @@ func (jc *jaasClient) ReadRelations(ctx context.Context, tuple *JaasTuple) ([]pa
jc.Errorf(err, "call to ListRelationshipTuples contained error(s)")
return nil, errors.New(resp.Errors[0])
}
relations = append(relations, resp.Tuples...)
relations = append(relations, toJaasTuples(resp.Tuples)...)
if resp.ContinuationToken == "" {
return relations, nil
}
Expand Down
1 change: 1 addition & 0 deletions internal/juju/jaas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ func (s *JaasSuite) TestReadRelations() {
relations, err := client.ReadRelations(context.Background(), &tuple)
s.Require().NoError(err)
s.Require().Len(relations, 2)
s.Require().Equal(relations, []JaasTuple{tuple, tuple})
}

func (s *JaasSuite) TestReadRelationsEmptyTuple() {
Expand Down
47 changes: 47 additions & 0 deletions internal/provider/expect_recreated_resource_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package provider

import (
"context"
"errors"
"fmt"

tfjson "github.com/hashicorp/terraform-json"
"github.com/hashicorp/terraform-plugin-testing/plancheck"
)

var _ plancheck.PlanCheck = expectRecreatedResource{}

type expectRecreatedResource struct {
resourceName string
}

// CheckPlan implements the plan check logic.
func (e expectRecreatedResource) CheckPlan(ctx context.Context, req plancheck.CheckPlanRequest, resp *plancheck.CheckPlanResponse) {
var result []error

for _, rc := range req.Plan.ResourceChanges {
if rc.Address == e.resourceName {
changes := rc.Change.Actions
if len(changes) != 2 {
result = append(result, fmt.Errorf("2 changes for resource %s (delete and create): %d found", rc.Address, len(changes)))
continue
}
if changes[0] != tfjson.ActionDelete && changes[1] != tfjson.ActionCreate {
result = append(result, fmt.Errorf("expected delete then create for resource %s, but found planned action(s): %v", rc.Address, rc.Change.Actions))
}
}
}

resp.Error = errors.Join(result...)
}

// expectRecreatedResource returns a plan check that asserts is a delete and create change present.
// All output and resource changes found will be aggregated and returned in a plan check error.
func ExpectRecreatedResource(resourceName string) plancheck.PlanCheck {
return expectRecreatedResource{
resourceName: resourceName,
}
}
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ func (p *jujuProvider) Resources(_ context.Context) []func() resource.Resource {
func() resource.Resource { return NewUserResource() },
func() resource.Resource { return NewSecretResource() },
func() resource.Resource { return NewAccessSecretResource() },
func() resource.Resource { return NewJAASAccessModelResource() },
}
}

Expand Down
10 changes: 10 additions & 0 deletions internal/provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,13 @@ func TestFrameworkProviderSchema(t *testing.T) {
assert.Equal(t, resp.Diagnostics.HasError(), false)
assert.Len(t, resp.Schema.Attributes, 6)
}

func expectedResourceOwner() string {
// Only 1 field is expected to be populated.
username := os.Getenv(JujuUsernameEnvKey)
clientId := os.Getenv(JujuClientIDEnvKey)
if clientId != "" {
clientId = clientId + "@serviceaccount"
}
return username + clientId
}
Loading

0 comments on commit d0a93a9

Please sign in to comment.