Skip to content

Commit

Permalink
add local user data source
Browse files Browse the repository at this point in the history
  • Loading branch information
chuyich committed Dec 6, 2023
1 parent d074c0a commit 06ad417
Show file tree
Hide file tree
Showing 13 changed files with 521 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## 1.1.0 ()

FEATURES:
* **New Data Source:** `ontap_protocols_cifs_local_user_data_source` ([#55](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/55))
* **New Data Source:** `ontap_protocols_cifs_local_users_data_source` ([#55](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/55))

ENHANCEMENTS:
* **netapp-ontap_protocols_nfs_export_policy_resource**: Add support for import ([#34](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/34))

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
data "netapp-ontap_protocols_cifs_local_user_data_source" "protocols_cifs_local_user" {
# required to know which system to interface with
cx_profile_name = "cluster4"
svm_name = "svm1"
name = "testme"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
data "netapp-ontap_protocols_cifs_local_users_data_source" "protocols_cifs_local_users" {
# required to know which system to interface with
cx_profile_name = "cluster4"
filter = {
name = "user1"
svm_name = "svm*"
}
}
136 changes: 136 additions & 0 deletions internal/interfaces/protocols_cifs_local_user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
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"
)

// TODO:
// copy this file to match you data source (should match internal/interfaces/protocols_cifs_local_user.go)
// replace CifsLocalUser with the name of the resource, following go conventions, eg IPInterface
// replace protocols_cifs_local_user with the name of the resource, for logging purposes, eg ip_interface
// replace api_url with API, eg ip/interfaces
// delete these 5 lines

// CifsLocalUserGetDataModelONTAP describes the GET record data model using go types for mapping.
type CifsLocalUserGetDataModelONTAP struct {
Name string `mapstructure:"name"`
SID string `mapstructure:"sid"`
SVM svm `mapstructure:"svm"`
FullName string `mapstructure:"full_name"`
Description string `mapstructure:"description"`
Membership []membership `mapstructure:"membership"`
AccountDisabled bool `mapstructure:"account_disabled"`
}

// Membership describes the membership data model using go types for mapping.
type membership struct {
Name string `mapstructure:"name"`
}

// CifsLocalUserDataSourceFilterModel describes the data source data model for queries.
type CifsLocalUserDataSourceFilterModel struct {
Name string `mapstructure:"name"`
SVMName string `mapstructure:"svm.name"`
}

// CifsLocalUserResourceBodyDataModelONTAP describes the body data model using go types for mapping.
type CifsLocalUserResourceBodyDataModelONTAP struct {
Name string `mapstructure:"name"`
SVM svm `mapstructure:"svm"`
}

// GetCifsLocalUser to get protocols_cifs_local_user info

Check failure on line 47 in internal/interfaces/protocols_cifs_local_user.go

View workflow job for this annotation

GitHub Actions / build

comment on exported function GetCifsLocalUserByName should be of the form "GetCifsLocalUserByName ..."
func GetCifsLocalUserByName(errorHandler *utils.ErrorHandler, r restclient.RestClient, name string, svmName string) (*CifsLocalUserGetDataModelONTAP, error) {
api := "protocols/cifs/local-users"
query := r.NewQuery()
query.Set("name", name)
query.Set("svm.name", svmName)

query.Fields([]string{"name", "svm.name", "full_name", "description", "membership", "account_disabled"})
statusCode, response, err := r.GetNilOrOneRecord(api, query, nil)
if err == nil && response == nil {
err = fmt.Errorf("no response for GET %s", api)
}
if err != nil {
return nil, errorHandler.MakeAndReportError("error reading protocols_cifs_local_user info", fmt.Sprintf("error on GET %s: %s, statusCode %d", api, err, statusCode))
}

var dataONTAP CifsLocalUserGetDataModelONTAP
if err := mapstructure.Decode(response, &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, response))
}
tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Read protocols_cifs_local_user data source: %#v", dataONTAP))
return &dataONTAP, nil
}

// GetCifsLocalUsers to get protocols_cifs_local_user info for all resources matching a filter
func GetCifsLocalUsers(errorHandler *utils.ErrorHandler, r restclient.RestClient, filter *CifsLocalUserDataSourceFilterModel) ([]CifsLocalUserGetDataModelONTAP, error) {
api := "protocols/cifs/local-users"
query := r.NewQuery()
query.Fields([]string{"name", "svm.name", "full_name", "description", "membership", "account_disabled"})

if filter != nil {
var filterMap map[string]interface{}
if err := mapstructure.Decode(filter, &filterMap); err != nil {
return nil, errorHandler.MakeAndReportError("error encoding protocols_cifs_local_users filter info", fmt.Sprintf("error on filter %#v: %s", filter, err))
}
query.SetValues(filterMap)
}
statusCode, response, err := r.GetZeroOrMoreRecords(api, query, nil)
if err == nil && response == nil {
err = fmt.Errorf("no response for GET %s", api)
}
if err != nil {
return nil, errorHandler.MakeAndReportError("error reading protocols_cifs_local_users info", fmt.Sprintf("error on GET %s: %s, statusCode %d", api, err, statusCode))
}

var dataONTAP []CifsLocalUserGetDataModelONTAP
for _, info := range response {
var record CifsLocalUserGetDataModelONTAP
if err := mapstructure.Decode(info, &record); err != nil {
return nil, errorHandler.MakeAndReportError(fmt.Sprintf("failed to decode response from GET %s", api),
fmt.Sprintf("error: %s, statusCode %d, info %#v", err, statusCode, info))
}
dataONTAP = append(dataONTAP, record)
}
tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Read protocols_cifs_local_users data source: %#v", dataONTAP))
return dataONTAP, nil
}

// CreateCifsLocalUser to create protocols_cifs_local_user
func CreateCifsLocalUser(errorHandler *utils.ErrorHandler, r restclient.RestClient, body CifsLocalUserResourceBodyDataModelONTAP) (*CifsLocalUserGetDataModelONTAP, error) {
api := "protocols/cifs/local-users"
var bodyMap map[string]interface{}
if err := mapstructure.Decode(body, &bodyMap); err != nil {
return nil, errorHandler.MakeAndReportError("error encoding protocols_cifs_local_user 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 protocols_cifs_local_user", fmt.Sprintf("error on POST %s: %s, statusCode %d", api, err, statusCode))
}

var dataONTAP CifsLocalUserGetDataModelONTAP
if err := mapstructure.Decode(response.Records[0], &dataONTAP); err != nil {
return nil, errorHandler.MakeAndReportError("error decoding protocols_cifs_local_user info", fmt.Sprintf("error on decode storage/protocols_cifs_local_users info: %s, statusCode %d, response %#v", err, statusCode, response))
}
tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Create protocols_cifs_local_user source - udata: %#v", dataONTAP))
return &dataONTAP, nil
}

// DeleteCifsLocalUser to delete protocols_cifs_local_user
func DeleteCifsLocalUser(errorHandler *utils.ErrorHandler, r restclient.RestClient, uuid string) error {
api := "protocols/cifs/local-users"
statusCode, _, err := r.CallDeleteMethod(api+"/"+uuid, nil, nil)
if err != nil {
return errorHandler.MakeAndReportError("error deleting protocols_cifs_local_user", fmt.Sprintf("error on DELETE %s: %s, statusCode %d", api, err, statusCode))
}
return nil
}
162 changes: 162 additions & 0 deletions internal/provider/protocols_cifs_local_user_data_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package provider

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/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 _ datasource.DataSource = &CifsLocalUserDataSource{}

// NewCifsLocalUserDataSource is a helper function to simplify the provider implementation.
func NewCifsLocalUserDataSource() datasource.DataSource {
return &CifsLocalUserDataSource{
config: resourceOrDataSourceConfig{
name: "protocols_cifs_local_user_data_source",
},
}
}

// CifsLocalUserDataSource defines the data source implementation.
type CifsLocalUserDataSource struct {
config resourceOrDataSourceConfig
}

// CifsLocalUserDataSourceModel describes the data source data model.
type CifsLocalUserDataSourceModel struct {
CxProfileName types.String `tfsdk:"cx_profile_name"`
Name types.String `tfsdk:"name"`
SVMName types.String `tfsdk:"svm_name"`
FullName types.String `tfsdk:"full_name"`
Description types.String `tfsdk:"description"`
Membership []Membership `tfsdk:"membership"`
AccountDisabled types.Bool `tfsdk:"account_disabled"`
ID types.String `tfsdk:"id"`
}

// Membership describes the membership data model.
type Membership struct {
Name types.String `tfsdk:"name"`
}

// Metadata returns the data source type name.
func (d *CifsLocalUserDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_" + d.config.name
}

// Schema defines the schema for the data source.
func (d *CifsLocalUserDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
// This description is used by the documentation generator and the language server.
MarkdownDescription: "CifsLocalUser data source",

Attributes: map[string]schema.Attribute{
"cx_profile_name": schema.StringAttribute{
MarkdownDescription: "Connection profile name",
Required: true,
},
"name": schema.StringAttribute{
MarkdownDescription: "CifsLocalUser name",
Required: true,
},
"svm_name": schema.StringAttribute{
MarkdownDescription: "IPInterface svm name",
Required: true,
},
"full_name": schema.StringAttribute{
Computed: true,
MarkdownDescription: "CifsLocalUser full name",
},
"description": schema.StringAttribute{
Computed: true,
MarkdownDescription: "CifsLocalUser description",
},
"membership": schema.ListNestedAttribute{
Computed: true,
MarkdownDescription: "CifsLocalUser membership",
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"name": schema.StringAttribute{
Computed: true,
MarkdownDescription: "CifsLocalUser membership name",
},
},
},
},
"account_disabled": schema.BoolAttribute{
Computed: true,
MarkdownDescription: "CifsLocalUser account disabled",
},
"id": schema.StringAttribute{
Computed: true,
MarkdownDescription: "CifsLocalUser id",
},
},
}
}

// Configure adds the provider configured client to the data source.
func (d *CifsLocalUserDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.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 Data Source Configure Type",
fmt.Sprintf("Expected Config, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
}
d.config.providerConfig = config
}

// Read refreshes the Terraform state with the latest data.
func (d *CifsLocalUserDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data CifsLocalUserDataSourceModel

// Read Terraform configuration data into the model
resp.Diagnostics.Append(req.Config.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, d.config, data.CxProfileName)
if err != nil {
// error reporting done inside NewClient
return
}

restInfo, err := interfaces.GetCifsLocalUserByName(errorHandler, *client, data.Name.ValueString(), data.SVMName.ValueString())
if err != nil {
// error reporting done inside GetCifsLocalUser
return
}
data.ID = types.StringValue(restInfo.SID)
data.SVMName = types.StringValue(restInfo.SVM.Name)
data.Name = types.StringValue(restInfo.Name)
data.FullName = types.StringValue(restInfo.FullName)
data.Description = types.StringValue(restInfo.Description)
data.AccountDisabled = types.BoolValue(restInfo.AccountDisabled)
data.Membership = make([]Membership, len(restInfo.Membership))
for i, m := range restInfo.Membership {
data.Membership[i].Name = types.StringValue(m.Name)
}

// Write logs using the tflog package
// Documentation: https://terraform.io/plugin/log
tflog.Debug(ctx, fmt.Sprintf("read a data source: %#v", data))

// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
Loading

0 comments on commit 06ad417

Please sign in to comment.