-
Notifications
You must be signed in to change notification settings - Fork 44
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 juju_access_offer resource #633
base: main
Are you sure you want to change the base?
Conversation
99e33e1
to
3b7b636
Compare
29c1c47
to
df0e20b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some initial comments. happy to talk about this, if needed.
}, | ||
"users": schema.SetAttribute{ | ||
Description: "List of users to grant access.", | ||
Optional: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this optional? without users, does an access offer resource make sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All attributes regarding users are optional but there is one validator for checking if at least one is set.
} | ||
|
||
// Get information from ID | ||
offerURL, _, _ := retrieveAccessOfferDataFromID(ctx, state.ID, state.Users, &resp.Diagnostics) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why ignore the access level from offer ID. i think you should use it to filter out users that don't have the matching access level.
continue | ||
} | ||
users = append(users, offerUserDetail.UserName) | ||
if access != "" && access != string(offerUserDetail.Access) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again, i don't think this is quite right. imagine an application offer where alice has admin access, bob has consume access and eve has read access, which is quite a valid state for an application offer. the way this condition is written, this would produce an error here, because we see three different access levels for the same offer.
internal/juju/offers.go
Outdated
return nil | ||
} | ||
|
||
// This function revokes access to an offer. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function revokes a specific level of access to an offer.
I think (correct me if i'm wrong), but access levels for offers work a bit differently in juju.
e.g. if a user has admin
access to an offer and we revoke admin
access for that user, the user is left with consume
access to the offer.. if we revoke consume
access from an admin
user, that user is left with read
access. and if we revoke read
access, user is left with no access to the offer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alesstimec you are correct in how revoke on offer works.
The provider should decide which level to grant or revoke, not the internal juju code making the API call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hmlanigan yes, but with the current schema, where juju_access_offer only lists users for a single access level, this is nearly impossible, since you can have a juju_access_offer resource for the consume
level and another one for read
level and the two don't "know" about eachother.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also the godoc is not formatted correctly.
|
||
A resource that represent a Juju Access Offer. | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
todo: add examples/resource/juju_access_offer
directory with resource.tf
and import.sh
files, content will be added to this markdown file during make install
.
"github.com/juju/juju/core/crossmodel" | ||
"github.com/juju/names/v5" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
todo: move the github.com/juju
imports to the second stanza, in sorted order.
resp.Diagnostics.AddError( | ||
"ImportState Failure", | ||
fmt.Sprintf("Malformed AccessOffer ID %q, "+ | ||
"please use format '<offer URL>:<access>:<user1,user1>'", IDstr), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thought: we're having issues with cross controller CMR today in this provider, however an offer URL with a controller uses a colon, e.g. foo:admin/another-model.postgresql
. A different delineator would be a good idea.
// Get user/access info from Offer | ||
response, err := a.client.Offers.ReadOffer(&juju.ReadOfferInput{ | ||
OfferURL: offerURL, | ||
}) | ||
if err != nil { | ||
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read offer, got error: %s", err)) | ||
return | ||
} | ||
a.trace(fmt.Sprintf("read juju offer %q", offerURL)) | ||
offerUsers := response.Users |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This data is available in state:
var state accessOfferResourceOffer
// Get the current state
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
Doing it this way has the potential to change access levels for the offer which were not set via this resource.
internal/juju/offers.go
Outdated
return nil | ||
} | ||
|
||
// This function revokes access to an offer. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alesstimec you are correct in how revoke on offer works.
The provider should decide which level to grant or revoke, not the internal juju code making the API call.
offerUserNames[offerUser.UserName] = struct{}{} | ||
if !slices.Contains(planUsers, offerUser.UserName) { | ||
a.trace(fmt.Sprintf("revoke user %q for offer %q", offerUser.UserName, offerURL)) | ||
err := a.client.Offers.RevokeOffer(&juju.GrantRevokeOfferInput{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a user has been removed from the resource, then to remove their privileges, you'd Revoke read
. Afaik, it's not necessary to step through the levels.
I've also suggested not allowing the admin
user as a valid user to avoid problems there.
_, err = client.ApplicationOffer(input.OfferURL) | ||
if err != nil { | ||
return err | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to do this. RevokeOffer will validate that the offer exists.
_, err = client.ApplicationOffer(input.OfferURL) | ||
if err != nil { | ||
return err | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to do this. GrantOffer will validate that the offer exists.
users := []string{} | ||
var access string | ||
for _, offerUserDetail := range response.Users { | ||
if offerUserDetail.UserName == "everyone@external" || offerUserDetail.UserName == "admin" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
todo: please document in the schema description the admin
user will not be handled via this resource. Suggest failing the validation of the user set as well if admin is specified.
type GrantRevokeOfferInput struct { | ||
User string | ||
Access string | ||
OfferURL string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be URL's, you can revoke many at once
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we discussed changing the schema in the last terraform office hours, I think this no longer apply, WDYT?
internal/juju/offers.go
Outdated
return nil | ||
} | ||
|
||
// This function revokes access to an offer. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also the godoc is not formatted correctly.
internal/juju/offers.go
Outdated
return err | ||
} | ||
|
||
err = client.RevokeOffer(input.User, input.Access, input.OfferURL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be (if we can make the schema look nice) input.OfferURLs
df0e20b
to
3fa9531
Compare
3fa9531
to
35e236a
Compare
Description
This is a PR for adding the juju_access_offer resource based on schema described in #627
Type of change
<What type of a change is this? Please keep one or more relevant lines from below and delete the others.>
Environment
Juju controller version: 3.5
Terraform version: 1.7.4
QA steps
Manual QA steps should be done to test this PR.
Additional notes
<Please add relevant notes & information, links to mattermost chats, other related issues/PRs, anything to help understand and QA the PR.>
Since Update changes the ID, I had to set Access as a RequiresReplace. Otherwise, tests fail with: