From b36584ce801770048fa692d695d4c7a527990f8a Mon Sep 17 00:00:00 2001 From: Adam Robinson Date: Thu, 15 Oct 2020 17:02:55 -0400 Subject: [PATCH 1/4] add cloud access data source --- rhsm/data_source_cloud_access.go | 156 +++++++++++++++++++++++++++++++ rhsm/provider.go | 1 + 2 files changed, 157 insertions(+) create mode 100644 rhsm/data_source_cloud_access.go diff --git a/rhsm/data_source_cloud_access.go b/rhsm/data_source_cloud_access.go new file mode 100644 index 0000000..19a6e07 --- /dev/null +++ b/rhsm/data_source_cloud_access.go @@ -0,0 +1,156 @@ +package rhsm + +import ( + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceCloudAccess() *schema.Resource { + return &schema.Resource{ + Read: dataSourceCloudAccessRead, + Schema: map[string]*schema.Schema{ + "enabled_accounts": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "accounts": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "date_added": { + Type: schema.TypeString, + Computed: true, + }, + "gold_image_status": { + Type: schema.TypeString, + Computed: true, + }, + "nickname": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "products": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled_quantity": { + Type: schema.TypeInt, + Computed: true, + }, + "image_groups": { + Type: schema.TypeList, + Computed: true, + Elem: schema.TypeString, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "next_renewal": { + Type: schema.TypeString, + Computed: true, + }, + "sku": { + Type: schema.TypeString, + Computed: true, + }, + "total_quantity": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "short_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceCloudAccessRead(d *schema.ResourceData, meta interface{}) error { + client, auth, err := meta.(*Config).Client() + if err != nil { + return err + } + + cap, _, err := client.CloudaccessApi.ListEnabledCloudAccessProviders(auth) + if err != nil { + return err + } + + cloudProviders := make([]map[string]interface{}, 0) + + for _, x := range cap.Body { + cloudProvider := make(map[string]interface{}) + cloudProvider["name"] = x.Name + cloudProvider["short_name"] = x.ShortName + + accounts := make([]map[string]interface{}, 0) + for _, y := range x.Accounts { + account := make(map[string]interface{}) + account["id"] = y.Id + account["nickname"] = y.Nickname + account["date_added"] = y.DateAdded + account["gold_image_status"] = y.GoldImageStatus + accounts = append(accounts, account) + } + cloudProvider["accounts"] = accounts + + products := make([]map[string]interface{}, 0) + for _, y := range x.Products { + product := make(map[string]interface{}) + product["name"] = y.Name + product["sku"] = y.Sku + product["enabled_quantity"] = y.EnabledQuantity + product["total_quantity"] = y.TotalQuantity + product["image_groups"] = y.ImageGroups + product["next_renewal"] = y.NextRenewal + products = append(products, product) + } + cloudProvider["products"] = products + + cloudProviders = append(cloudProviders, cloudProvider) + } + + // for _, x := range alloc.Body.EntitlementsAttached.Value { + // if x.Id == entitlementID { + // entitlementFound = true + // d.Set("contract_number", x.ContractNumber) + // d.Set("quantity", x.EntitlementQuantity) + // d.Set("sku", x.Sku) + + // } + // } + + d.SetId(time.Now().UTC().String()) + + d.Set("enabled_accounts", cloudProviders) + + // entitlementsAttached := make(map[string]interface{}) + // entitlementsAttached["reason"] = alloc.Body.EntitlementsAttached.Reason + // entitlementsAttached["valid"] = alloc.Body.EntitlementsAttached.Valid + // entitlementsAttachedList := []map[string]interface{}{entitlementsAttached} + // d.Set("entitlements_attached", entitlementsAttachedList) + + return nil +} diff --git a/rhsm/provider.go b/rhsm/provider.go index b123fa7..347fb60 100644 --- a/rhsm/provider.go +++ b/rhsm/provider.go @@ -32,6 +32,7 @@ func Provider() *schema.Provider { "rhsm_allocation": dataSourceAllocation(), "rhsm_allocation_entitlement": dataSourceAllocationEntitlement(), "rhsm_allocation_pools": dataSourceAllocationPools(), + "rhsm_cloud_access": dataSourceCloudAccess(), }, ConfigureFunc: providerConfigure, } From 61665a89c4cc15a363fbd295cb9360fc1a225a86 Mon Sep 17 00:00:00 2001 From: Adam Robinson Date: Fri, 16 Oct 2020 10:19:10 -0400 Subject: [PATCH 2/4] cleanup --- rhsm/data_source_cloud_access.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/rhsm/data_source_cloud_access.go b/rhsm/data_source_cloud_access.go index 19a6e07..ab46785 100644 --- a/rhsm/data_source_cloud_access.go +++ b/rhsm/data_source_cloud_access.go @@ -132,25 +132,9 @@ func dataSourceCloudAccessRead(d *schema.ResourceData, meta interface{}) error { cloudProviders = append(cloudProviders, cloudProvider) } - // for _, x := range alloc.Body.EntitlementsAttached.Value { - // if x.Id == entitlementID { - // entitlementFound = true - // d.Set("contract_number", x.ContractNumber) - // d.Set("quantity", x.EntitlementQuantity) - // d.Set("sku", x.Sku) - - // } - // } - d.SetId(time.Now().UTC().String()) d.Set("enabled_accounts", cloudProviders) - // entitlementsAttached := make(map[string]interface{}) - // entitlementsAttached["reason"] = alloc.Body.EntitlementsAttached.Reason - // entitlementsAttached["valid"] = alloc.Body.EntitlementsAttached.Valid - // entitlementsAttachedList := []map[string]interface{}{entitlementsAttached} - // d.Set("entitlements_attached", entitlementsAttachedList) - return nil } From cf62119283b63057e4d65ff164bea1ab3f1ad5b7 Mon Sep 17 00:00:00 2001 From: Adam Robinson Date: Fri, 16 Oct 2020 12:50:01 -0400 Subject: [PATCH 3/4] add rhsm_cloud_access_account resource --- rhsm/provider.go | 1 + rhsm/resource_cloud_access_account.go | 228 ++++++++++++++++++++++++++ 2 files changed, 229 insertions(+) create mode 100644 rhsm/resource_cloud_access_account.go diff --git a/rhsm/provider.go b/rhsm/provider.go index 347fb60..ffd5079 100644 --- a/rhsm/provider.go +++ b/rhsm/provider.go @@ -27,6 +27,7 @@ func Provider() *schema.Provider { "rhsm_allocation": resourceAllocation(), "rhsm_allocation_entitlement": resourceAllocationEntitlement(), "rhsm_allocation_manifest": resourceAllocationManifest(), + "rhsm_cloud_access_account": resourceCloudAccessAccount(), }, DataSourcesMap: map[string]*schema.Resource{ "rhsm_allocation": dataSourceAllocation(), diff --git a/rhsm/resource_cloud_access_account.go b/rhsm/resource_cloud_access_account.go new file mode 100644 index 0000000..64397c1 --- /dev/null +++ b/rhsm/resource_cloud_access_account.go @@ -0,0 +1,228 @@ +package rhsm + +import ( + "github.com/antihax/optional" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/umich-vci/gorhsm" +) + +func resourceCloudAccessAccount() *schema.Resource { + return &schema.Resource{ + Create: resourceCloudAccessAccountCreate, + Read: resourceCloudAccessAccountRead, + Update: resourceCloudAccessAccountUpdate, + Delete: resourceCloudAccessAccountDelete, + Schema: map[string]*schema.Schema{ + "account_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "provider_short_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"AWS", "GCE", "MSAZ"}, false), + }, + "gold_images": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "nickname": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "", + }, + "date_added": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "gold_image_status": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceCloudAccessAccountRead(d *schema.ResourceData, meta interface{}) error { + client, auth, err := meta.(*Config).Client() + if err != nil { + return err + } + + id := d.Get("account_id").(string) + shortName := d.Get("provider_short_name").(string) + foundAccount := false + + cap, _, err := client.CloudaccessApi.ListEnabledCloudAccessProviders(auth) + if err != nil { + return err + } + + for _, x := range cap.Body { + if x.ShortName == shortName { + for _, y := range x.Accounts { + if y.Id == id { + foundAccount = true + d.Set("nickname", y.Nickname) + d.Set("date_added", y.DateAdded) + d.Set("gold_image_status", y.GoldImageStatus) + break + } + } + } + } + + if !foundAccount { + d.SetId("") + } + + return nil +} + +func resourceCloudAccessAccountCreate(d *schema.ResourceData, meta interface{}) error { + client, auth, err := meta.(*Config).Client() + if err != nil { + return err + } + + id := d.Get("account_id").(string) + shortName := d.Get("provider_short_name").(string) + nickname := d.Get("nickname").(string) + + account := &gorhsm.AddProviderAccount{ + Id: id, + Nickname: nickname, + } + accountList := []gorhsm.AddProviderAccount{*account} + accountOpts := &gorhsm.AddProviderAccountsOpts{ + Account: optional.NewInterface(accountList), + } + + _, err = client.CloudaccessApi.AddProviderAccounts(auth, shortName, accountOpts) + if err != nil { + return err + } + + d.SetId(id) + + if g, ok := d.GetOk("gold_images"); ok { + rawGoldImages := g.(*schema.Set).List() + goldimages := []string{} + for x := range rawGoldImages { + goldimages = append(goldimages, rawGoldImages[x].(string)) + } + gi := &gorhsm.InlineObject2{ + Accounts: []string{id}, + Images: goldimages, + } + goldopts := &gorhsm.EnableGoldImagesOpts{ + GoldImages: optional.NewInterface(*gi), + } + + _, err = client.CloudaccessApi.EnableGoldImages(auth, shortName, goldopts) + if err != nil { + d.Set("gold_images", []string{}) + return err + } + } + + return nil +} + +func resourceCloudAccessAccountUpdate(d *schema.ResourceData, meta interface{}) error { + client, auth, err := meta.(*Config).Client() + if err != nil { + return err + } + + id := d.Id() + shortName := d.Get("provider_short_name").(string) + + update := &gorhsm.InlineObject{ + Id: id, + } + + idOrNameChange := false + + if d.HasChange("account_id") { + idOrNameChange = true + update.NewID = d.Get("account_id").(string) + } + + if d.HasChange("nickname") { + idOrNameChange = true + update.NewNickname = d.Get("nickname").(string) + } + + updateOpts := &gorhsm.UpdateProviderAccountOpts{ + Account: optional.NewInterface(*update), + } + + if idOrNameChange { + _, err = client.CloudaccessApi.UpdateProviderAccount(auth, shortName, updateOpts) + if err != nil { + return err + } + + if d.HasChange("account_id") { + d.SetId(d.Get("account_id").(string)) + } + } + + if d.HasChange("gold_images") { + if g, ok := d.GetOk("gold_images"); ok { + rawGoldImages := g.(*schema.Set).List() + goldimages := []string{} + for x := range rawGoldImages { + goldimages = append(goldimages, rawGoldImages[x].(string)) + } + gi := &gorhsm.InlineObject2{ + Accounts: []string{id}, + Images: goldimages, + } + goldopts := &gorhsm.EnableGoldImagesOpts{ + GoldImages: optional.NewInterface(*gi), + } + + _, err = client.CloudaccessApi.EnableGoldImages(auth, shortName, goldopts) + if err != nil { + d.Set("gold_images", []string{}) + return err + } + } + } + + return nil +} + +func resourceCloudAccessAccountDelete(d *schema.ResourceData, meta interface{}) error { + client, auth, err := meta.(*Config).Client() + if err != nil { + return err + } + + id := d.Id() + shortName := d.Get("provider_short_name").(string) + + remove := &gorhsm.InlineObject1{ + Id: id, + } + removeOpts := &gorhsm.RemoveProviderAccountOpts{ + Account: optional.NewInterface(remove), + } + + _, err = client.CloudaccessApi.RemoveProviderAccount(auth, shortName, removeOpts) + if err != nil { + return err + } + + d.SetId("") + + return nil +} From b48f930aff8acb5fdaa8a6345149d1eb604f45d8 Mon Sep 17 00:00:00 2001 From: Adam Robinson Date: Fri, 16 Oct 2020 14:16:24 -0400 Subject: [PATCH 4/4] update documentation --- README.md | 7 ++-- docs/data-sources/cloud_access.md | 44 ++++++++++++++++++++++++++ docs/resources/cloud_access_account.md | 33 +++++++++++++++++++ 3 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 docs/data-sources/cloud_access.md create mode 100644 docs/resources/cloud_access_account.md diff --git a/README.md b/README.md index cf58c23..d65dcfd 100644 --- a/README.md +++ b/README.md @@ -9,19 +9,20 @@ A Terraform provider for Red Hat Subscription Manager (RHSM) This provider can be used to create and manage allocations, entitlements, and manifests for Red Hat Subscriptions that can be used with Red Hat Satellite. -Support will be added in the future to manage Red Hat Cloud Access entitlements for cloud accounts. +It can also be used to manage Red Hat Cloud Access for various cloud provider accounts. Requesting access to +gold images is supported as well, but presently is only supported on Azure by Red Hat. The provider does not have working tests so it should probably be considered beta. ## Building/Installing -The provider is now available on the [Terraform Registry](https://registry.terraform.io/providers/umich-vci/rhsm/latest) so you probably don't need to build the provider. +The provider is now available on the [Terraform Registry](https://registry.terraform.io/providers/umich-vci/rhsm/latest) so you probably don't need to build the provider unless you want to contribute. That said, running `GO111MODULE=on go get -u github.com/umich-vci/terraform-provider-rhsm` should download the code and result in a binary at `$GOPATH/bin/terraform-provider-rhsm`. You can then move the binary to `~/.terraform.d/plugins` to use it with Terraform. -This has been tested with Terraform 0.12.x and Red Hat Satellite 6.6.x and 6.7.x. +This has been tested with Terraform 0.12.x. ## License diff --git a/docs/data-sources/cloud_access.md b/docs/data-sources/cloud_access.md new file mode 100644 index 0000000..92c90c5 --- /dev/null +++ b/docs/data-sources/cloud_access.md @@ -0,0 +1,44 @@ +# rhsm\_cloud\_access Data Source + +Use this data source to look up information about cloud providers entitled to Red Hat Cloud Access. + +## Example Usage + +```hcl +data "rhsm_cloud_access" "ca" {} +``` + +## Attributes Reference + +* `enabled_accounts` - A list where each entry is a single cloud provider + +In each element of `enabled_accounts` are the following attributes: + +* `accounts` - A list of cloud accounts that are enabled for cloud access in the cloud provider. + + * `id` - The id of the cloud + + * `date_added` - The date the account was added to cloud access. + + * `gold_image_status` - The status of any requests for gold image access for a cloud account. + + * `nickname` - A nickname associated with the cloud account. + + * `name` - The name of the cloud provider. + +* `products` - A list of products that are entitled to the cloud provider. + + * `enabled_quantity` - The quantity of subscriptions allowed to be consumed by the cloud provider. + + * `image_groups` - A list of images associated with the cloud provider. These are used when + requesting access to gold images for a cloud account. + + * `name` - The name of the product. + + * `next_renewal` - The renewal date of the subscription. + + * `sku` - The SKU of the product. + + * `total_quantity` - The total number of subscriptions of the product available. + +* `short_name` - An abreviation of the cloud provider name. Used when adding or removing accounts. diff --git a/docs/resources/cloud_access_account.md b/docs/resources/cloud_access_account.md new file mode 100644 index 0000000..5fee529 --- /dev/null +++ b/docs/resources/cloud_access_account.md @@ -0,0 +1,33 @@ +# rhsm\_cloud\_access\_account Resource + +Use this resource to create an entitlement for Red Hat Cloud Access for an account in a supported cloud provider. + +## Example Usage + +```hcl +resource "rhsm_cloud_access_account" "test_account" { + account_id = "123e4567-e89b-12d3-a456-426614174000" + provider_short_name = "MSAZ" + nickname = "Test Account" + gold_images = ["rhel-byos"] +} +``` + +## Argument Reference + +* `account_id` - (Required) The ID of a cloud account that you would like to request Red Hat Cloud Access for. + +* `provider_short_name` - (Required) The short name of the cloud provider that the `account_id` is in. + This must be one of "AWS", "GCE", or "MSAZ". Other cloud providers are supported but have not been tested + so they are not in the list of valid options. + +* `nickname` - (Optional) A nickname to help describe the account. The default is an empty string. + +* `gold_images` - (Optional) A list of gold images to request access to for the account. + Images available to a cloud provider can be found with the `rhsm_cloud_access` data source. + +## Attributes Reference + +* `date_added` - The date the cloud account was added to Red Hat Cloud Access. + +* `gold_image_status` - The status of requests for access to gold images.