From 5626a359089f4b31ca1bafbccde839d364f5df2d Mon Sep 17 00:00:00 2001 From: Chris Archibald Date: Wed, 26 Jul 2023 20:06:22 +0000 Subject: [PATCH] Sync bitbucket and GitHub --- .github/ISSUE_TEMPLATE/00_bug_report.yml | 6 +- .github/ISSUE_TEMPLATE/01_documentation.yml | 6 +- .github/ISSUE_TEMPLATE/02_enhancement.yml | 4 +- .github/ISSUE_TEMPLATE/03_new_resource.yml | 11 +- .github/workflows/lint.yml | 2 +- .github/workflows/reportcard.yml | 2 +- .github/workflows/unit-test.yml | 2 +- .../cluster_licensing_license_resource.md | 39 +++ .../provider.tf | 44 ++++ .../resource.tf | 5 + .../variables.tf | 11 + .../interfaces/cluster_licensing_license.go | 88 +++++++ .../cluster_licensing_license_test.go | 57 ++++ .../cluster_licensing_license_resource.go | 247 ++++++++++++++++++ internal/provider/provider.go | 1 + scripts/generate_docs.py | 3 +- 16 files changed, 512 insertions(+), 16 deletions(-) create mode 100644 docs/resources/cluster_licensing_license_resource.md create mode 100644 examples/resources/netapp-ontap_cluster_licensing_license/provider.tf create mode 100644 examples/resources/netapp-ontap_cluster_licensing_license/resource.tf create mode 100644 examples/resources/netapp-ontap_cluster_licensing_license/variables.tf create mode 100644 internal/interfaces/cluster_licensing_license.go create mode 100644 internal/provider/acceptancetests/cluster_licensing_license_test.go create mode 100644 internal/provider/cluster_licensing_license_resource.go diff --git a/.github/ISSUE_TEMPLATE/00_bug_report.yml b/.github/ISSUE_TEMPLATE/00_bug_report.yml index 8e144bbc..82e67ff3 100644 --- a/.github/ISSUE_TEMPLATE/00_bug_report.yml +++ b/.github/ISSUE_TEMPLATE/00_bug_report.yml @@ -59,7 +59,7 @@ body: attributes: label: Affected Resource(s) description: Please list the affected resource(s) and/or data source(s). - placeholder: "* ONTAP_xxx" + placeholder: "* netapp-ontap_xxx" validations: required: false @@ -98,8 +98,6 @@ body: Terraform configurations or debug logs under 25MB may be [attached directly in the field](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/attaching-files). Files larger than this should be uploaded to a file hosting service and a link shared. - For your security you can also encrypt the files using our [GPG public key](https://keybase.io/hashicorp). - **Note:** These form fields do not automatically render, so you will need to use [code fence(s)](https://help.github.com/articles/basic-writing-and-formatting-syntax/#quoting-code) to achieve proper formatting. - type: textarea @@ -170,7 +168,7 @@ body: attributes: label: Would you like to implement a fix? description: | - If you plan to implement a fix for this, check this box to let the maintainers and community know (you can update this later if you change your mind). If this would be your first contribution, refer to the [contribution guide](https://hashicorp.github.io/terraform-provider-ONTAP/) for tips on getting started. + If you plan to implement a fix for this, check this box to let the maintainers and community know (you can update this later if you change your mind). If this would be your first contribution, refer to the ::TODO:: [contribution guide](https://hashicorp.github.io/terraform-provider-ONTAP/) for tips on getting started. options: - "No" - "Yes" diff --git a/.github/ISSUE_TEMPLATE/01_documentation.yml b/.github/ISSUE_TEMPLATE/01_documentation.yml index aba030b4..21159534 100644 --- a/.github/ISSUE_TEMPLATE/01_documentation.yml +++ b/.github/ISSUE_TEMPLATE/01_documentation.yml @@ -8,9 +8,9 @@ body: value: | # Thank you for raising a documentation issue! - This form is meant to alert the maintainers to an issue with the provider documentation found on the [Terraform Registry](https://registry.terraform.io/providers/hashicorp/ONTAP/latest) (such as resource and data source documentation, guides and examples), as well as the [contribution guide](https://hashicorp.github.io/terraform-provider-ONTAP/). + This form is meant to alert the maintainers to an issue with the provider documentation found on the ::TODO:: [Terraform Registry](https://registry.terraform.io/providers/hashicorp/ONTAP/latest) (such as resource and data source documentation, guides and examples), as well as the ::TODO:: [contribution guide](https://hashicorp.github.io/terraform-provider-ONTAP/). - Documentation edits are generally a bit less involved, so are often a great entrypoint if you've ever been interested in [contributing](https://hashicorp.github.io/terraform-provider-ONTAP/documentation-changes/)! + Documentation edits are generally a bit less involved, so are often a great entrypoint if you've ever been interested in ::TODO:: [contributing](https://hashicorp.github.io/terraform-provider-ONTAP/documentation-changes/)! - type: input id: registry_link @@ -44,7 +44,7 @@ body: attributes: label: Would you like to implement a fix? description: | - If you plan to implement a fix for this, check this box to let the maintainers and community know (you can update this later if you change your mind). If this would be your first contribution, refer to the [contribution guide](https://hashicorp.github.io/terraform-provider-ONTAP/) for tips on getting started. + If you plan to implement a fix for this, check this box to let the maintainers and community know (you can update this later if you change your mind). If this would be your first contribution, refer to the ::TODO:: [contribution guide](https://hashicorp.github.io/terraform-provider-ONTAP/) for tips on getting started. options: - "No" - "Yes" diff --git a/.github/ISSUE_TEMPLATE/02_enhancement.yml b/.github/ISSUE_TEMPLATE/02_enhancement.yml index 5e20f087..997d674c 100644 --- a/.github/ISSUE_TEMPLATE/02_enhancement.yml +++ b/.github/ISSUE_TEMPLATE/02_enhancement.yml @@ -24,7 +24,7 @@ body: id: affected attributes: label: Affected Resource(s) and/or Data Source(s) - placeholder: "* ONTAP_xxx_yyy" + placeholder: "* netapp_ontap_xxx_yyy" validations: required: false @@ -56,7 +56,7 @@ body: attributes: label: Would you like to implement a fix? description: | - If you plan to implement a fix for this, check this box to let the maintainers and community know (you can update this later if you change your mind). If this would be your first contribution, refer to the [contribution guide](https://hashicorp.github.io/terraform-provider-ONTAP/) for tips on getting started. + If you plan to implement a fix for this, check this box to let the maintainers and community know (you can update this later if you change your mind). If this would be your first contribution, refer to the ::TODO:: [contribution guide](https://hashicorp.github.io/terraform-provider-ONTAP/) for tips on getting started. options: - "No" - "Yes" diff --git a/.github/ISSUE_TEMPLATE/03_new_resource.yml b/.github/ISSUE_TEMPLATE/03_new_resource.yml index e5b63f5f..c615e6a9 100644 --- a/.github/ISSUE_TEMPLATE/03_new_resource.yml +++ b/.github/ISSUE_TEMPLATE/03_new_resource.yml @@ -9,13 +9,18 @@ body: This form is intended to be used when requesting an entirely new resource, data source, or service be added to the provider. If you're looking for a change to be made to an existing resource or data source, consider submitting either the "Request an Enhancement" or Report a Bug" forms instead. - When possible, it's helpful to check the [ONTAP Go SDK](https://pkg.go.dev/github.com/ONTAP/ONTAP-sdk-go/service) or [ONTAP Go SDK v2](https://pkg.go.dev/github.com/ONTAP/ONTAP-sdk-go-v2#section-directories) to determine whether functionality exists to enable the requested feature. It is **not** required that you do this. Any references found when searching can be added to the "References" field below to give maintainers or the community a head start. + When possible, it's helpful to check the [ONTAP REST API](https://library.netapp.com/ecmdocs/ECMLP2885799/html/index.html) to determine whether functionality exists to enable the requested feature. It is **not** required that you do this. Any references found when searching can be added to the "References" field below to give maintainers or the community a head start. Please update the title to match what you're requesting, e.g.: - `[New Resource]:` - for new resource requests - `[New Data Source]:` - for new data source requests - `[New Service]:` - for new ONTAP services + + Please add a label on the right to match what your requesting, e.g.: + + - `New Resource` - for new resource requests + - `New Data Source` - for new data source requests - type: textarea id: description @@ -33,7 +38,7 @@ body: Please list any new resource(s) and/or data source(s). The naming format is `ONTAP__`, e.g., `ONTAP_lambda_function`. A best guess is helpful, even if you're unsure of exactly what the end result will look like. This helps maintainers and the community better understand how you (someone who is in need of this request) envisions it. - placeholder: "* ONTAP_xxx_yyy" + placeholder: "* netapp_ontap_xxx_yyy" validations: required: true @@ -65,7 +70,7 @@ body: attributes: label: Would you like to implement a fix? description: | - If you plan to implement a fix for this, check this box to let the maintainers and community know (you can update this later if you change your mind). If this would be your first contribution, refer to the [contribution guide](https://hashicorp.github.io/terraform-provider-ONTAP/) for tips on getting started. + If you plan to implement a fix for this, check this box to let the maintainers and community know (you can update this later if you change your mind). If this would be your first contribution, refer to the ::TODO:: [contribution guide](https://hashicorp.github.io/terraform-provider-ONTAP/) for tips on getting started. options: - "No" - "Yes" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c5821c02..94b13c3f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,4 +17,4 @@ jobs: - name: Build run: | go install golang.org/x/lint/golint@latest - golint -set_exit_status ./... + golint -set_exit_status ./... \ No newline at end of file diff --git a/.github/workflows/reportcard.yml b/.github/workflows/reportcard.yml index 13f00409..bb042e94 100644 --- a/.github/workflows/reportcard.yml +++ b/.github/workflows/reportcard.yml @@ -22,4 +22,4 @@ jobs: make install go install ./cmd/goreportcard-cli - name: run report card - run: goreportcard-cli -v -t 90 + run: goreportcard-cli -v -t 90 \ No newline at end of file diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 706e3a25..c3f79505 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -17,4 +17,4 @@ jobs: - name: Build run: | export GOFLAGS=-buildvcs=false - ./scripts/unittest.sh + ./scripts/unittest.sh \ No newline at end of file diff --git a/docs/resources/cluster_licensing_license_resource.md b/docs/resources/cluster_licensing_license_resource.md new file mode 100644 index 00000000..60723440 --- /dev/null +++ b/docs/resources/cluster_licensing_license_resource.md @@ -0,0 +1,39 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "ONTAP: Licensing_License" +subcategory: "cluster" +description: |- + Add/Remove License from ONTAP +--- + +# Resource licensing_license + +Add/Remove License from ONTAP + +## Example Usage + +```terraform +resource "netapp-ontap_cluster_licensing_license_resource" "cluster_licensing_license" { + # required to know which system to interface with + cx_profile_name = "cluster4" + keys = ["testme"] +``` + + + +## Schema + +### Required + +- `cx_profile_name` (String) Connection profile name +- `keys` (List of String) List of NLF or 26-character keys + +### Read-Only + +- `id` (String) The ID of this resource. +- `name` (String) Name of the license +- `scope` (String) Scope of the license +- `serial_number` (String) +- `state` (String) State of the license + + diff --git a/examples/resources/netapp-ontap_cluster_licensing_license/provider.tf b/examples/resources/netapp-ontap_cluster_licensing_license/provider.tf new file mode 100644 index 00000000..ac399a3f --- /dev/null +++ b/examples/resources/netapp-ontap_cluster_licensing_license/provider.tf @@ -0,0 +1,44 @@ +terraform { + required_providers { + netapp-ontap = { + source = "NetApp/netapp-ontap" + version = "0.0.1" + } + } +} + + +provider "netapp-ontap" { + # A connection profile defines how to interface with an ONTAP cluster or vserver. + # At least one is required. + connection_profiles = [ + { + name = "cluster1" + hostname = "********219" + username = var.username + password = var.password + validate_certs = var.validate_certs + }, + { + name = "cluster2" + hostname = "********222" + username = var.username + password = var.password + validate_certs = var.validate_certs + }, + { + name = "cluster3" + hostname = "10.193.176.159" + username = var.username + password = var.password + validate_certs = var.validate_certs + }, + { + name = "cluster4" + hostname = "10.193.180.108" + username = var.username + password = var.password + validate_certs = var.validate_certs + } + ] +} diff --git a/examples/resources/netapp-ontap_cluster_licensing_license/resource.tf b/examples/resources/netapp-ontap_cluster_licensing_license/resource.tf new file mode 100644 index 00000000..1b3ce59a --- /dev/null +++ b/examples/resources/netapp-ontap_cluster_licensing_license/resource.tf @@ -0,0 +1,5 @@ +resource "netapp-ontap_cluster_licensing_license_resource" "cluster_licensing_license" { + # required to know which system to interface with + cx_profile_name = "cluster4" + keys = ["testme"] +} diff --git a/examples/resources/netapp-ontap_cluster_licensing_license/variables.tf b/examples/resources/netapp-ontap_cluster_licensing_license/variables.tf new file mode 100644 index 00000000..b79f07ed --- /dev/null +++ b/examples/resources/netapp-ontap_cluster_licensing_license/variables.tf @@ -0,0 +1,11 @@ +# Terraform will prompt for values, unless a tfvars file is present. +variable "username" { + type = string +} +variable "password" { + type = string + sensitive = true +} +variable "validate_certs" { + type = bool +} diff --git a/internal/interfaces/cluster_licensing_license.go b/internal/interfaces/cluster_licensing_license.go new file mode 100644 index 00000000..b286dab0 --- /dev/null +++ b/internal/interfaces/cluster_licensing_license.go @@ -0,0 +1,88 @@ +package interfaces + +import ( + "fmt" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/mitchellh/mapstructure" + "github.com/netapp/terraform-provider-netapp-ontap/internal/restclient" + "github.com/netapp/terraform-provider-netapp-ontap/internal/utils" +) + +// ClusterLicensingLicenseKeyDataModelONTAP a single record from cluster/licensing/licenses +type ClusterLicensingLicenseKeyDataModelONTAP struct { + Name string `mapstructure:"name"` + Scope string `mapstructure:"scope"` + State string `mapstructure:"state"` + Licenses []ClusterLicensingLicenseLicensesDataModelONTAP `mapstructure:"licenses"` +} + +// ClusterLicensingLicenseLicensesDataModelONTAP a single serial number +type ClusterLicensingLicenseLicensesDataModelONTAP struct { + SerialNumber string `mapstructure:"serial_number"` +} + +// ClusterLicensingLicenseResourceBodyDataModelONTAP describes the body data model using go types for mapping. +type ClusterLicensingLicenseResourceBodyDataModelONTAP struct { + Keys []string `mapstructure:"keys"` +} + +// GetClusterLicensingLicenses to get /cluster/licensing/licenses info +func GetClusterLicensingLicenses(errorHandler *utils.ErrorHandler, r restclient.RestClient) ([]ClusterLicensingLicenseKeyDataModelONTAP, error) { + api := "/cluster/licensing/licenses" + query := r.NewQuery() + query.Fields([]string{"name", "state", "licenses"}) + statusCode, records, err := r.GetZeroOrMoreRecords(api, query, nil) + if err == nil && records == nil { + err = fmt.Errorf("no response for GET %s", api) + } + if err != nil { + return nil, errorHandler.MakeAndReportError("error reading /cluster/licensing/licenses info", fmt.Sprintf("error on GET %s: %s, statusCode %d", api, err, statusCode)) + } + + var dataONTAP ClusterLicensingLicenseKeyDataModelONTAP + keys := []ClusterLicensingLicenseKeyDataModelONTAP{} + for _, record := range records { + if err := mapstructure.Decode(record, &dataONTAP); err != nil { + return nil, errorHandler.MakeAndReportError(fmt.Sprintf("failed to decode response from GET %s", api), + fmt.Sprintf("error: %s, statusCode %d, response %#v", err, statusCode, record)) + } + keys = append(keys, dataONTAP) + } + tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Read /cluster/licensing/licenses data source: %#v", dataONTAP)) + return keys, nil +} + +// CreateClusterLicensingLicense to create /cluster/licensing/licenses +func CreateClusterLicensingLicense(errorHandler *utils.ErrorHandler, r restclient.RestClient, body ClusterLicensingLicenseResourceBodyDataModelONTAP) (*ClusterLicensingLicenseKeyDataModelONTAP, error) { + api := "/cluster/licensing/licenses" + var bodyMap map[string]interface{} + if err := mapstructure.Decode(body, &bodyMap); err != nil { + return nil, errorHandler.MakeAndReportError("error encoding /cluster/licensing/licenses body", fmt.Sprintf("error on encoding %s body: %s, body: %#v", api, err, body)) + } + query := r.NewQuery() + query.Add("return_records", "true") + statusCode, response, err := r.CallCreateMethod(api, query, bodyMap) + if err != nil { + return nil, errorHandler.MakeAndReportError("error creating /cluster/licensing/licenses", fmt.Sprintf("error on POST %s: %s, statusCode %d", api, err, statusCode)) + } + + var dataONTAP ClusterLicensingLicenseKeyDataModelONTAP + // TODO: Fix it may be possible for a Key to unlock mutiple keys + if err := mapstructure.Decode(response.Records[0], &dataONTAP); err != nil { + return nil, errorHandler.MakeAndReportError("error decoding /cluster/licensing/licenses info", fmt.Sprintf("error on decode storage//cluster/licensing/licensess info: %s, statusCode %d, response %#v", err, statusCode, response)) + } + tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Create /cluster/licensing/licenses source - udata: %#v", dataONTAP)) + return &dataONTAP, nil +} + +// DeleteClusterLicensingLicense to delete /cluster/licensing/licenses +func DeleteClusterLicensingLicense(errorHandler *utils.ErrorHandler, r restclient.RestClient, name string, serialNumber string) error { + api := "/cluster/licensing/licenses" + query := r.NewQuery() + query.Add("serial_number", serialNumber) + statusCode, _, err := r.CallDeleteMethod(api+"/"+name, query, nil) + if err != nil { + return errorHandler.MakeAndReportError("error deleting /cluster/licensing/licenses", fmt.Sprintf("error on DELETE %s: %s, statusCode %d", api, err, statusCode)) + } + return nil +} diff --git a/internal/provider/acceptancetests/cluster_licensing_license_test.go b/internal/provider/acceptancetests/cluster_licensing_license_test.go new file mode 100644 index 00000000..cd4a4b3e --- /dev/null +++ b/internal/provider/acceptancetests/cluster_licensing_license_test.go @@ -0,0 +1,57 @@ +package acceptancetests + +import ( + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "os" + "regexp" + "testing" +) + +func TestLicensingLicenseResouce(t *testing.T) { + testLicense := os.Getenv("TF_ACC_NETAPP_LICENSE") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccLicensingLicenseResourceConfig("testme"), + ExpectError: regexp.MustCompile("1115159"), + }, + { + Config: testAccLicensingLicenseResourceConfig(testLicense), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("netapp-ontap_cluster_licensing_license_resource.cluster_licensing_license", "name", "insight_balance")), + }, + }, + }) +} + +func testAccLicensingLicenseResourceConfig(key string) string { + host := os.Getenv("TF_ACC_NETAPP_HOST") + admin := os.Getenv("TF_ACC_NETAPP_USER") + password := os.Getenv("TF_ACC_NETAPP_PASS") + if host == "" || admin == "" || password == "" { + fmt.Println("TF_ACC_NETAPP_HOST, TF_ACC_NETAPP_USER, and TF_ACC_NETAPP_PASS must be set for acceptance tests") + os.Exit(1) + } + return fmt.Sprintf(` +provider "netapp-ontap" { + connection_profiles = [ + { + name = "cluster4" + hostname = "%s" + username = "%s" + password = "%s" + validate_certs = false + }, + ] +} + +resource "netapp-ontap_cluster_licensing_license_resource" "cluster_licensing_license" { + # required to know which system to interface with + cx_profile_name = "cluster4" + keys = ["%s"] +} +`, host, admin, password, key) +} diff --git a/internal/provider/cluster_licensing_license_resource.go b/internal/provider/cluster_licensing_license_resource.go new file mode 100644 index 00000000..ee1f5244 --- /dev/null +++ b/internal/provider/cluster_licensing_license_resource.go @@ -0,0 +1,247 @@ +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/netapp/terraform-provider-netapp-ontap/internal/interfaces" + "github.com/netapp/terraform-provider-netapp-ontap/internal/utils" +) + +// Ensure provider defined types fully satisfy framework interfaces +var _ resource.Resource = &ClusterLicensingLicenseResource{} +var _ resource.ResourceWithImportState = &ClusterLicensingLicenseResource{} + +// NewClusterLicensingLicenseResource is a helper function to simplify the provider implementation. +func NewClusterLicensingLicenseResource() resource.Resource { + return &ClusterLicensingLicenseResource{ + config: resourceOrDataSourceConfig{ + name: "cluster_licensing_license_resource", + }, + } +} + +// ClusterLicensingLicenseResource defines the resource implementation. +type ClusterLicensingLicenseResource struct { + config resourceOrDataSourceConfig +} + +// ClusterLicensingLicenseResourceModel describes the resource data model. +type ClusterLicensingLicenseResourceModel struct { + CxProfileName types.String `tfsdk:"cx_profile_name"` + Keys []types.String `tfsdk:"keys"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Scope types.String `tfsdk:"scope"` + State types.String `tfsdk:"state"` + SerialNumber types.String `tfsdk:"serial_number"` +} + +// Metadata returns the resource type name. +func (r *ClusterLicensingLicenseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_" + r.config.name +} + +// Schema defines the schema for the resource. +func (r *ClusterLicensingLicenseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + // This description is used by the documentation generator and the language server. + MarkdownDescription: "ClusterLicensingLicense resource", + + Attributes: map[string]schema.Attribute{ + "cx_profile_name": schema.StringAttribute{ + MarkdownDescription: "Connection profile name", + Required: true, + }, + "keys": schema.ListAttribute{ + Required: true, + MarkdownDescription: "List of NLF or 26-character keys", + ElementType: types.StringType, + }, + "name": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Name of the license", + }, + "scope": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Scope of the license", + }, + "state": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "State of the license", + }, + "serial_number": schema.StringAttribute{ + Computed: true, + }, + "id": schema.StringAttribute{ + Computed: true, + }, + }, + } +} + +// Configure adds the provider configured client to the resource. +func (r *ClusterLicensingLicenseResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + config, ok := req.ProviderData.(Config) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected Config, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + } + r.config.providerConfig = config +} + +// Create a resource and retrieve UUID +func (r *ClusterLicensingLicenseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data *ClusterLicensingLicenseResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + var body interfaces.ClusterLicensingLicenseResourceBodyDataModelONTAP + errorHandler := utils.NewErrorHandler(ctx, &resp.Diagnostics) + + if resp.Diagnostics.HasError() { + return + } + + client, err := getRestClient(errorHandler, r.config, data.CxProfileName) + if err != nil { + // error reporting done inside NewClient + return + } + + var keys []string + for _, v := range data.Keys { + keys = append(keys, v.ValueString()) + } + body.Keys = keys + resource, err := interfaces.CreateClusterLicensingLicense(errorHandler, *client, body) + if err != nil { + return + } + if resource == nil { + return // TODO: Add error + } + data.Name = types.StringValue(resource.Name) + data.Scope = types.StringValue(resource.Scope) + data.State = types.StringValue(resource.State) + data.ID = types.StringValue(resource.Name) + data.SerialNumber = types.StringValue(resource.Licenses[0].SerialNumber) // TODO: Double check there is only ever 1 + + tflog.Trace(ctx, "created a resource") + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +// Read refreshes the Terraform state with the latest data. +func (r *ClusterLicensingLicenseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data ClusterLicensingLicenseResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + errorHandler := utils.NewErrorHandler(ctx, &resp.Diagnostics) + // we need to defer setting the client until we can read the connection profile name + client, err := getRestClient(errorHandler, r.config, data.CxProfileName) + if err != nil { + // error reporting done inside NewClient + return + } + + restInfo, err := interfaces.GetClusterLicensingLicenses(errorHandler, *client) + if err != nil { + // error reporting done inside GetClusterLicensingLicense + return + } + if restInfo == nil { + return // TODO: Fix + } + + var matchingLicense interfaces.ClusterLicensingLicenseKeyDataModelONTAP + + for _, item := range restInfo { + if data.Name.ValueString() == item.Name { + matchingLicense = item + } + } + + data.Name = types.StringValue(matchingLicense.Name) + data.State = types.StringValue(matchingLicense.State) + data.Scope = types.StringValue(matchingLicense.Scope) + data.SerialNumber = types.StringValue(matchingLicense.Licenses[0].SerialNumber) // TODO: Double check there is only ever 1 + + // Write logs using the tflog package + // Documentation: https://terraform.io/plugin/log + tflog.Debug(ctx, fmt.Sprintf("read a resource: %#v", data)) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +// Update updates the resource and sets the updated Terraform state on success. +func (r *ClusterLicensingLicenseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data *ClusterLicensingLicenseResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + errorHandler := utils.NewErrorHandler(ctx, &resp.Diagnostics) + // License updates are not supported + err := errorHandler.MakeAndReportError("Update not supported for License", "Update not supported for License") + if err != nil { + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +// Delete deletes the resource and removes the Terraform state on success. +func (r *ClusterLicensingLicenseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data *ClusterLicensingLicenseResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + errorHandler := utils.NewErrorHandler(ctx, &resp.Diagnostics) + client, err := getRestClient(errorHandler, r.config, data.CxProfileName) + if err != nil { + // error reporting done inside NewClient + return + } + + err = interfaces.DeleteClusterLicensingLicense(errorHandler, *client, data.Name.ValueString(), data.SerialNumber.ValueString()) + if err != nil { + return + } + +} + +// ImportState imports a resource using ID from terraform import command by calling the Read method. +func (r *ClusterLicensingLicenseResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 120dec87..c40ce2c6 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -142,6 +142,7 @@ func (p *ONTAPProvider) Configure(ctx context.Context, req provider.ConfigureReq // Resources defines the provider's resources. func (p *ONTAPProvider) Resources(ctx context.Context) []func() resource.Resource { return []func() resource.Resource{ + NewClusterLicensingLicenseResource, NewClusterScheduleResource, NewExampleResource, NewExportPolicyResource, diff --git a/scripts/generate_docs.py b/scripts/generate_docs.py index 5c3526f2..76128d77 100755 --- a/scripts/generate_docs.py +++ b/scripts/generate_docs.py @@ -10,7 +10,8 @@ 'cluster': [ "cluster_data_source.md", "cluster_schedule_data_source.md", - "cluster_schedule_resource.md"], + "cluster_schedule_resource.md", + "cluster_licensing_license_resource.md"], 'nas': [ "protocols_nfs_service_data_source.md", "protocols_nfs_service_resource.md",