Skip to content

Commit

Permalink
feat: add juju_access_offer resource
Browse files Browse the repository at this point in the history
  • Loading branch information
amandahla committed Jan 9, 2025
1 parent fbd1ccc commit dde253e
Show file tree
Hide file tree
Showing 8 changed files with 881 additions and 2 deletions.
50 changes: 50 additions & 0 deletions docs/resources/access_offer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "juju_access_offer Resource - terraform-provider-juju"
subcategory: ""
description: |-
A resource that represent a Juju Access Offer. Warning: Do not repeat users across different access levels.
---

# juju_access_offer (Resource)

A resource that represent a Juju Access Offer. Warning: Do not repeat users across different access levels.

## Example Usage

```terraform
resource "juju_access_offer" "this" {
offer_url = juju_offer.my_application_offer.url
consume = [juju_user.dev.name]
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `offer_url` (String) The url of the offer for access management. If this is changed the resource will be deleted and a new resource will be created.

### Optional

- `admin` (Set of String) List of users to grant admin access. "admin" user is not allowed.
- `consume` (Set of String) List of users to grant consume access. "admin" user is not allowed.
- `read` (Set of String) List of users to grant read access. "admin" user is not allowed.

### Read-Only

- `id` (String) The ID of this resource.

## Import

Import is supported using the following syntax:

```shell
# Access Offers can be imported by using the Offer URL as in the juju show-offers output.
# Example:
# $juju show-offer mysql
# Store URL Access Description Endpoint Interface Role
# mycontroller admin/db.mysql admin MariaDB Server is one of the most ... mysql mysql provider
$ terraform import juju_access_offer.db admin/db.mysql
```
6 changes: 6 additions & 0 deletions examples/resources/juju_access_offer/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Access Offers can be imported by using the Offer URL as in the juju show-offers output.
# Example:
# $juju show-offer mysql
# Store URL Access Description Endpoint Interface Role
# mycontroller admin/db.mysql admin MariaDB Server is one of the most ... mysql mysql provider
$ terraform import juju_access_offer.db admin/db.mysql
4 changes: 4 additions & 0 deletions examples/resources/juju_access_offer/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
resource "juju_access_offer" "this" {
offer_url = juju_offer.my_application_offer.url
consume = [juju_user.dev.name]
}
66 changes: 64 additions & 2 deletions internal/juju/offers.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type ReadOfferResponse struct {
ModelName string
Name string
OfferURL string
Users []crossmodel.OfferUserDetails
}

type DestroyOfferInput struct {
Expand All @@ -74,12 +75,20 @@ type RemoveRemoteOfferInput struct {
OfferURL string
}

// GrantRevokeOfferInput represents input for granting or revoking access to an offer.
type GrantRevokeOfferInput struct {
Users []string
Access string
OfferURL string
}

func newOffersClient(sc SharedClient) *offersClient {
return &offersClient{
SharedClient: sc,
}
}

// CreateOffer creates offer managed by the offer resource.
func (c offersClient) CreateOffer(input *CreateOfferInput) (*CreateOfferResponse, []error) {
var errs []error

Expand Down Expand Up @@ -152,6 +161,7 @@ func (c offersClient) CreateOffer(input *CreateOfferInput) (*CreateOfferResponse
return &resp, nil
}

// ReadOffer reads offer managed by the offer resource.
func (c offersClient) ReadOffer(input *ReadOfferInput) (*ReadOfferResponse, error) {
conn, err := c.GetConnection(nil)
if err != nil {
Expand All @@ -170,6 +180,7 @@ func (c offersClient) ReadOffer(input *ReadOfferInput) (*ReadOfferResponse, erro
response.ApplicationName = result.ApplicationName
response.OfferURL = result.OfferURL
response.Endpoint = result.Endpoints[0].Name
response.Users = result.Users

//no model name is returned but it can be parsed from the resulting offer URL to ensure parity
//TODO: verify if we can fetch information another way
Expand All @@ -182,6 +193,7 @@ func (c offersClient) ReadOffer(input *ReadOfferInput) (*ReadOfferResponse, erro
return &response, nil
}

// DestroyOffer destroys offer managed by the offer resource.
func (c offersClient) DestroyOffer(input *DestroyOfferInput) error {
conn, err := c.GetConnection(nil)
if err != nil {
Expand Down Expand Up @@ -249,7 +261,7 @@ func parseModelFromURL(url string) (result string, success bool) {
return result, true
}

// This function allows the integration resource to consume the offers managed by the offer resource
// ConsumeRemoteOffer allows the integration resource to consume the offers managed by the offer resource.
func (c offersClient) ConsumeRemoteOffer(input *ConsumeRemoteOfferInput) (*ConsumeRemoteOfferResponse, error) {
modelConn, err := c.GetConnection(&input.ModelName)
if err != nil {
Expand Down Expand Up @@ -330,7 +342,7 @@ func (c offersClient) ConsumeRemoteOffer(input *ConsumeRemoteOfferInput) (*Consu
return &response, nil
}

// This function allows the integration resource to destroy the offers managed by the offer resource
// RemoveRemoteOffer allows the integration resource to destroy the offers managed by the offer resource.
func (c offersClient) RemoveRemoteOffer(input *RemoveRemoteOfferInput) []error {
var errors []error
conn, err := c.GetConnection(&input.ModelName)
Expand Down Expand Up @@ -390,3 +402,53 @@ func (c offersClient) RemoveRemoteOffer(input *RemoveRemoteOfferInput) []error {

return nil
}

// GrantOffer adds access to an offer managed by the access offer resource.
// No action or error if the access was already granted to the user.
func (c offersClient) GrantOffer(input *GrantRevokeOfferInput) error {
conn, err := c.GetConnection(nil)
if err != nil {
return err
}
defer func() { _ = conn.Close() }()

client := applicationoffers.NewClient(conn)

for _, user := range input.Users {
err = client.GrantOffer(user, input.Access, input.OfferURL)
if err != nil {
// ignore if user was already granted
if strings.Contains(err.Error(), "user already has") {
continue
}
return err
}
}

return nil
}

// RevokeOffer revokes access to an offer managed by the access offer resource.
// No action or error if the access was already revoked for the user.
func (c offersClient) RevokeOffer(input *GrantRevokeOfferInput) error {
conn, err := c.GetConnection(nil)
if err != nil {
return err
}
defer func() { _ = conn.Close() }()

client := applicationoffers.NewClient(conn)

for _, user := range input.Users {
err = client.RevokeOffer(user, input.Access, input.OfferURL)
if err != nil {
// ignore if user was already revoked
if strings.Contains(err.Error(), "not found") {
continue
}
return err
}
}

return nil
}
1 change: 1 addition & 0 deletions internal/provider/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const (

LogResourceApplication = "resource-application"
LogResourceAccessModel = "resource-access-model"
LogResourceAccessOffer = "resource-access-offer"
LogResourceCredential = "resource-credential"
LogResourceKubernetesCloud = "resource-kubernetes-cloud"
LogResourceMachine = "resource-machine"
Expand Down
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ func getJujuProviderModel(ctx context.Context, req provider.ConfigureRequest) (j
func (p *jujuProvider) Resources(_ context.Context) []func() resource.Resource {
return []func() resource.Resource{
func() resource.Resource { return NewAccessModelResource() },
func() resource.Resource { return NewAccessOfferResource() },
func() resource.Resource { return NewApplicationResource() },
func() resource.Resource { return NewCredentialResource() },
func() resource.Resource { return NewIntegrationResource() },
Expand Down
Loading

0 comments on commit dde253e

Please sign in to comment.