diff --git a/.github/workflows/blackduck.yml b/.github/workflows/blackduck.yml new file mode 100644 index 00000000..7ee1ad46 --- /dev/null +++ b/.github/workflows/blackduck.yml @@ -0,0 +1,29 @@ +name: Blackduck Scan + +on: + push: + branches: + - integration/main + +jobs: + build: + + runs-on: self-hosted + steps: + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' # See 'Supported distributions' for available options + java-version: '17' + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.19.3' + - uses: actions/checkout@v3 + + - name: Build + env: + BLACKDUCK_URL: ${{ secrets.BLACKDUCK_URL }} + BLACKDUCK_API_TOKEN: ${{ secrets.BLACKDUCK_API_TOKEN }} + + run: | + bash scripts/bd_scan.bash $BLACKDUCK_URL $BLACKDUCK_API_TOKEN diff --git a/CHANGELOG.md b/CHANGELOG.md index 62c7f9d0..9fa6ae49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ ENHANCEMENTS: * **netapp-ontap_svm_resource**: Add support for import ([#6](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/6)) * **netapp-ontap_storage_volume_snapshot_resource**: Add support for import ([#42](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/42)) * **netapp-ontap_cluster_schedule_resource**: Add support for import ([#31](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/31)) +* **netapp-ontap_snapmiror_policy**: Add support for import ([#38](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/38)) +* **netapp-ontap_networking_ip_interface_resource**: Add support for import ([#32](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/32)) +* **netapp-ontap_protocols_nfs_export_policy_rule_resource**: Add support for import ([#35](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/35)) ## 1.0.2 (2023-11-17) diff --git a/docs/resources/networking_ip_interface_resource.md b/docs/resources/networking_ip_interface_resource.md index 2f2ba419..cc25d4c1 100644 --- a/docs/resources/networking_ip_interface_resource.md +++ b/docs/resources/networking_ip_interface_resource.md @@ -68,4 +68,39 @@ Required: - `home_port` (String) IPInterface home port ## Import -Import is currently not support for this Resource. +This Resource supports import, which allows you to import existing network ip interface into the state of this resoruce. +Import require a unique ID composed of the interface name, svm_name and cx_profile_name, separated by a comma. + id = `name`,`svm_name`,`cx_profile_name` + ### Terraform Import + For example + ```shell + terraform import netapp-ontap_networking_ip_interface_resource.example if1,svm1,cluster4 + ``` + +!> The terraform import CLI command can only import resources into the state. Importing via the CLI does not generate configuration. If you want to generate the accompanying configuration for imported resources, use the import block instead. + +### Terrafomr Import Block +This requires Terraform 1.5 or higher, and will auto create the configuration for you + +First create the block +```terraform +import { + to = netapp-ontap_networking_ip_interface_resource.if_import + id = "if1,svm1,cluster4" +} +``` +Next run, this will auto create the configuration for you +```shell +terraform plan -generate-config-out=generated.tf +``` +This will generate a file called generated.tf, which will contain the configuration for the imported resource +```terraform +# __generated__ by Terraform +# Please review these resources and move them into your main configuration files. +# __generated__ by Terraform from "if1,svm1,cluster4" +resource "netapp-ontap_networking_ip_interface_resource" "if1_import" { + cx_profile_name = "cluster4" + name = "if1" + svm_name = "svm1" +} +``` diff --git a/docs/resources/protocols_nfs_export_policy_rule_resource.md b/docs/resources/protocols_nfs_export_policy_rule_resource.md index c3f12f97..21d2a2ff 100644 --- a/docs/resources/protocols_nfs_export_policy_rule_resource.md +++ b/docs/resources/protocols_nfs_export_policy_rule_resource.md @@ -59,5 +59,39 @@ resource "netapp-ontap_protocols_nfs_export_policy_rule_resource" "example" { - `index` (Number) rule index ## Import -Import is currently not support for this Resource. +This Resource supports import, which allows you to import existing nfs export policy rule into the state of this resoruce. +Import require a unique ID composed of the rule index, export policy name, svm_name and cx_profile_name, separated by a comma. + id = `index`,`export_policy_name`,`svm_name`,`cx_profile_name` + ### Terraform Import + For example + ```shell + terraform import netapp-ontap_protocols_nfs_export_policy_rule_resource.rule_import index1,exp1,svm1,cluster4 + ``` +!> The terraform import CLI command can only import resources into the state. Importing via the CLI does not generate configuration. If you want to generate the accompanying configuration for imported resources, use the import block instead. + +### Terrafomr Import Block +This requires Terraform 1.5 or higher, and will auto create the configuration for you + +First create the block +```terraform +import { + to = netapp-ontap_protocols_nfs_export_policy_rule_resource.rule_import + id = "index1,exp1,svm1,cluster4" +} +``` +Next run, this will auto create the configuration for you +```shell +terraform plan -generate-config-out=generated.tf +``` +This will generate a file called generated.tf, which will contain the configuration for the imported resource +```terraform +# __generated__ by Terraform +# Please review these resources and move them into your main configuration files. +# __generated__ by Terraform from "index1,exp1,svm1,cluster4" +resource "netapp-ontap_protocols_nfs_export_policy_rule_resource" "rule_import" { + cx_profile_name = "cluster4" + export_policy_name = "exp1" + svm_name = "svm1" +} +``` \ No newline at end of file diff --git a/docs/resources/snapmirror_policy_resource.md b/docs/resources/snapmirror_policy_resource.md index fbb0d2c7..e98f4d60 100644 --- a/docs/resources/snapmirror_policy_resource.md +++ b/docs/resources/snapmirror_policy_resource.md @@ -119,4 +119,58 @@ Optional: - `prefix` (String) Specifies the prefix for the Snapshot copy name to be created as per the schedule ## Import -Import is currently not support for this Resource. +This resource supports import, which allows you to import existing snapmirror policy into the state of this resource. +Import require a unique ID composed of the snapmirror policy name, svm name and connection profile, separated by a comma. + +id = `name`,`svm_name`,`cx_profile_name` + +### Terraform Import + +For example +```shell + terraform import netapp-ontap_snapmirror_policy_resource.example test_name,svm1,cluster5 +``` +!> The terraform import CLI command can only import resources into the state. Importing via the CLI does not generate configuration. If you want to generate the accompanying configuration for imported resources, use the import block instead. + +### Terrafomr Import Block +This requires Terraform 1.5 or higher, and will auto create the configuration for you + +First create the block +```terraform +import { + to = netapp-ontap_snapmirror_policy_resource.snapmirror_policy_import + id = "test_name,svm1,cluster4" +} +``` +Next run, this will auto create the configuration for you +```shell +terraform plan -generate-config-out=generated.tf +``` +This will generate a file called generated.tf, which will contain the configuration for the imported resource +```terraform +# __generated__ by Terraform +# Please review these resources and move them into your main configuration files. +# __generated__ by Terraform from "svm1_root,svm1,cluster4" +resource "netapp-ontap_snapmirror_policy_resource" "svm_import" { + comment = "testing import" + cx_profile_name = "cluster4" + name = "testImport" + copy_all_source_snapshots = false + copy_latest_source_snapshot = false + create_snapshot_on_source = false + identity_preservation = full + network_compression_enabled = false + retention = [ + { + count = 4 + creation_schedule_name = null + label = hi + prefix = null + } + ] + svm_name = ansibleSVM + sync_type = null + transfer_schedule_name = null + type = async +} +``` diff --git a/internal/interfaces/networking_ip_interface.go b/internal/interfaces/networking_ip_interface.go index 4fdf1f96..f5d562e5 100644 --- a/internal/interfaces/networking_ip_interface.go +++ b/internal/interfaces/networking_ip_interface.go @@ -69,7 +69,35 @@ type IPInterfaceDataSourceFilterModel struct { } // GetIPInterface to get ip_interface info -func GetIPInterface(errorHandler *utils.ErrorHandler, r restclient.RestClient, name string, svmName string) (*IPInterfaceGetDataModelONTAP, error) { +func GetIPInterface(errorHandler *utils.ErrorHandler, r restclient.RestClient, id string) (*IPInterfaceGetDataModelONTAP, error) { + api := "network/ip/interfaces" + "/" + id + query := r.NewQuery() + // if svmName == "" { + // query.Set("scope", "cluster") + // } else { + // query.Set("svm.name", svmName) + // query.Set("scope", "svm") + // } + query.Fields([]string{"name", "svm.name", "ip", "scope", "location"}) + 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 ip_interface info", fmt.Sprintf("error on GET %s: %s, statusCode %d", api, err, statusCode)) + } + + var dataONTAP IPInterfaceGetDataModelONTAP + 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 ip_interface data source: %#v", dataONTAP)) + return &dataONTAP, nil +} + +// GetIPInterfaceByName to get ip_interface info +func GetIPInterfaceByName(errorHandler *utils.ErrorHandler, r restclient.RestClient, name string, svmName string) (*IPInterfaceGetDataModelONTAP, error) { api := "network/ip/interfaces" query := r.NewQuery() query.Set("name", name) diff --git a/internal/interfaces/networking_ip_interface_test.go b/internal/interfaces/networking_ip_interface_test.go index 4ce06bf2..4e83779a 100644 --- a/internal/interfaces/networking_ip_interface_test.go +++ b/internal/interfaces/networking_ip_interface_test.go @@ -130,7 +130,7 @@ func TestGetIPInterface(t *testing.T) { if err != nil { panic(err) } - got, err := GetIPInterface(errorHandler, *r, "name", "svmName") + got, err := GetIPInterfaceByName(errorHandler, *r, "name", "svmName") if err != nil { fmt.Printf("err: %s\n", err) } diff --git a/internal/interfaces/snapmirror_policy.go b/internal/interfaces/snapmirror_policy.go index 554646d6..1c8e4624 100644 --- a/internal/interfaces/snapmirror_policy.go +++ b/internal/interfaces/snapmirror_policy.go @@ -130,7 +130,7 @@ func GetSnapmirrorPolicy(errorHandler *utils.ErrorHandler, r restclient.RestClie } // GetSnapmirrorPolicyByName to get snapmirror policy info -func GetSnapmirrorPolicyByName(errorHandler *utils.ErrorHandler, r restclient.RestClient, name string, svmName string) (*SnapmirrorPolicyGetDataModelONTAP, error) { +func GetSnapmirrorPolicyByName(errorHandler *utils.ErrorHandler, r restclient.RestClient, name string, svmName string) (*SnapmirrorPolicyGetRawDataModelONTAP, error) { api := "snapmirror/policies" query := r.NewQuery() query.Set("name", name) @@ -141,7 +141,7 @@ func GetSnapmirrorPolicyByName(errorHandler *utils.ErrorHandler, r restclient.Re query.Set("scope", "svm") } // TODO: copy_all_source_snapshots is 9.10 and up - query.Fields(([]string{"name", "svm.name", "type", "comment", "transfer_schedule", "network_compression_enabled", "retention", "identity_preservation", "copy_all_source_snapshots", "uuid"})) + query.Fields(([]string{"name", "svm.name", "type", "sync_type", "comment", "transfer_schedule", "network_compression_enabled", "retention", "identity_preservation", "copy_all_source_snapshots", "uuid"})) statusCode, response, err := r.GetNilOrOneRecord(api, query, nil) if err == nil && response == nil { err = fmt.Errorf("no response for GET %s", api) @@ -150,7 +150,7 @@ func GetSnapmirrorPolicyByName(errorHandler *utils.ErrorHandler, r restclient.Re return nil, errorHandler.MakeAndReportError("error reading snapmirror/policies info", fmt.Sprintf("error on GET %s: %s, statusCode %d", api, err, statusCode)) } - var dataONTAP SnapmirrorPolicyGetDataModelONTAP + var dataONTAP SnapmirrorPolicyGetRawDataModelONTAP 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)) diff --git a/internal/provider/networking_ip_interface_data_source.go b/internal/provider/networking_ip_interface_data_source.go index 735ab8d8..ebd9f9ab 100644 --- a/internal/provider/networking_ip_interface_data_source.go +++ b/internal/provider/networking_ip_interface_data_source.go @@ -145,7 +145,7 @@ func (d *IPInterfaceDataSource) Read(ctx context.Context, req datasource.ReadReq return } - restInfo, err := interfaces.GetIPInterface(errorHandler, *client, data.Name.ValueString(), data.SVMName.ValueString()) + restInfo, err := interfaces.GetIPInterfaceByName(errorHandler, *client, data.Name.ValueString(), data.SVMName.ValueString()) if err != nil { // error reporting done inside GetIPInterface return diff --git a/internal/provider/networking_ip_interface_resource.go b/internal/provider/networking_ip_interface_resource.go index f0a9ea41..0594c94f 100644 --- a/internal/provider/networking_ip_interface_resource.go +++ b/internal/provider/networking_ip_interface_resource.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "strconv" + "strings" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -162,10 +163,19 @@ func (r *IPInterfaceResource) Read(ctx context.Context, req resource.ReadRequest return } - restInfo, err := interfaces.GetIPInterface(errorHandler, *client, data.Name.ValueString(), data.SVMName.ValueString()) - if err != nil { - // error reporting done inside GetIPInterface - return + var restInfo *interfaces.IPInterfaceGetDataModelONTAP + if data.UUID.IsNull() { + restInfo, err = interfaces.GetIPInterfaceByName(errorHandler, *client, data.Name.ValueString(), data.SVMName.ValueString()) + if err != nil { + // error reporting done inside GetIPInterfaceByName + return + } + } else { + restInfo, err = interfaces.GetIPInterface(errorHandler, *client, data.UUID.ValueString()) + if err != nil { + // error reporting done inside GetIPInterface + return + } } if restInfo == nil { errorHandler.MakeAndReportError("No Interface found", fmt.Sprintf("NO interface, %s found.", data.Name.ValueString())) @@ -173,15 +183,21 @@ func (r *IPInterfaceResource) Read(ctx context.Context, req resource.ReadRequest } data.Name = types.StringValue(restInfo.Name) data.UUID = types.StringValue(restInfo.UUID) - data.Location.HomeNode = types.StringValue(restInfo.Location.HomeNode.Name) - data.IP.Address = types.StringValue(restInfo.IP.Address) + + var location IPInterfaceResourceLocation + location.HomeNode = types.StringValue(restInfo.Location.HomeNode.Name) + location.HomePort = types.StringValue(restInfo.Location.HomePort.Name) + data.Location = &location + + var ip IPInterfaceResourceIP + ip.Address = types.StringValue(restInfo.IP.Address) intValue, err := strconv.Atoi(restInfo.IP.Netmask) if err != nil { errorHandler.MakeAndReportError("Failed to read ip interface", fmt.Sprintf("Error: failed to convert string value '%s' to int for net mask.", restInfo.IP.Netmask)) return } - data.IP.Netmask = types.Int64Value(int64(intValue)) - + ip.Netmask = types.Int64Value(int64(intValue)) + data.IP = &ip // Write logs using the tflog package // Documentation: https://terraform.io/plugin/log tflog.Debug(ctx, fmt.Sprintf("read a resource: %#v", data)) @@ -300,7 +316,7 @@ func (r *IPInterfaceResource) Delete(ctx context.Context, req resource.DeleteReq } if data.UUID.IsNull() { - errorHandler.MakeAndReportError("UUID is null", "ip_interface UUID is null") + errorHandler.MakeAndReportError("UUID is null", "ip_interface ID is null") return } @@ -313,5 +329,16 @@ func (r *IPInterfaceResource) Delete(ctx context.Context, req resource.DeleteReq // ImportState imports a resource using ID from terraform import command by calling the Read method. func (r *IPInterfaceResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + tflog.Debug(ctx, fmt.Sprintf("import req a network ip interface resource: %#v", req)) + idParts := strings.Split(req.ID, ",") + if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" { + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("Expected import identifier with format: name,svm_name,cx_profile_name. Got: %q", req.ID), + ) + return + } + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("name"), idParts[0])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("svm_name"), idParts[1])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("cx_profile_name"), idParts[2])...) } diff --git a/internal/provider/networking_ip_interface_resource_test.go b/internal/provider/networking_ip_interface_resource_test.go index 68fd41a1..619f1d0e 100644 --- a/internal/provider/networking_ip_interface_resource_test.go +++ b/internal/provider/networking_ip_interface_resource_test.go @@ -40,6 +40,16 @@ func TestAccNetworkingIpInterfaceResource(t *testing.T) { resource.TestCheckResourceAttr("netapp-ontap_networking_ip_interface_resource.example", "ip.address", "10.10.10.20"), ), }, + // Test importing a resource + { + ResourceName: "netapp-ontap_networking_ip_interface_resource.example", + ImportState: true, + ImportStateId: fmt.Sprintf("%s,%s,%s", "test-interface", "svm0", "cluster4"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("netapp-ontap_networking_ip_interface_resource.example", "name", "test-interface"), + resource.TestCheckResourceAttr("netapp-ontap_networking_ip_interface_resource.example", "ip.address", "10.10.10.20"), + ), + }, }, }) } diff --git a/internal/provider/protocols_nfs_export_policy_rule_resource.go b/internal/provider/protocols_nfs_export_policy_rule_resource.go index ef070c0a..75f1b8e9 100644 --- a/internal/provider/protocols_nfs_export_policy_rule_resource.go +++ b/internal/provider/protocols_nfs_export_policy_rule_resource.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "strconv" + "strings" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/path" @@ -354,6 +355,7 @@ func (r *ExportPolicyRuleResource) Read(ctx context.Context, req resource.ReadRe return } exportPolicyID = strconv.Itoa(exportPolicy.ID) + data.ExportPolicyID = types.StringValue(strconv.Itoa(exportPolicy.ID)) } else { exportPolicyID = data.ExportPolicyID.ValueString() } @@ -385,28 +387,18 @@ func (r *ExportPolicyRuleResource) Read(ctx context.Context, req resource.ReadRe data.Protocols = protocols data.Superuser = superuser + data.ID = types.StringValue(fmt.Sprintf("%s_%s_%s_%d", data.CxProfileName.ValueString(), data.SVMName.ValueString(), data.ExportPolicyName.ValueString(), data.Index.ValueInt64())) + for _, e := range restInfo.ClientsMatch { clientsMatch = append(clientsMatch, types.StringValue(e.Match)) } data.ClientsMatch = clientsMatch - - if !data.AllowDeviceCreation.IsNull() { - data.AllowDeviceCreation = types.BoolValue(restInfo.AllowDeviceCreation) - } - - if !data.AllowSuid.IsNull() { - data.AllowSuid = types.BoolValue(restInfo.AllowSuid) - } - - if !data.ChownMode.IsNull() { - data.ChownMode = types.StringValue(restInfo.ChownMode) - } - if !data.NtfsUnixSecurity.IsNull() { - data.NtfsUnixSecurity = types.StringValue(restInfo.NtfsUnixSecurity) - } - if !data.AnonymousUser.IsNull() { - data.AnonymousUser = types.StringValue(restInfo.AnonymousUser) - } + // update optional params containg devaule values with updated values + data.AllowDeviceCreation = types.BoolValue(restInfo.AllowDeviceCreation) + data.AllowSuid = types.BoolValue(restInfo.AllowSuid) + data.ChownMode = types.StringValue(restInfo.ChownMode) + data.NtfsUnixSecurity = types.StringValue(restInfo.NtfsUnixSecurity) + data.AnonymousUser = types.StringValue(restInfo.AnonymousUser) // Save updated data into Terraform state resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) @@ -537,5 +529,18 @@ func (r *ExportPolicyRuleResource) Delete(ctx context.Context, req resource.Dele // ImportState imports a resource using ID from terraform import command by calling the Read method. func (r *ExportPolicyRuleResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + tflog.Debug(ctx, fmt.Sprintf("import req a nfs export policy rule resource: %#v", req)) + idParts := strings.Split(req.ID, ",") + if len(idParts) != 4 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" || idParts[3] == "" { + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("Expected import identifier with format: index,export_policy_name,svm_name,cx_profile_name. Got: %q", req.ID), + ) + return + } + index, _ := strconv.Atoi(idParts[0]) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("index"), index)...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("export_policy_name"), idParts[1])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("svm_name"), idParts[2])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("cx_profile_name"), idParts[3])...) } diff --git a/internal/provider/protocols_nfs_export_policy_rule_resource_test.go b/internal/provider/protocols_nfs_export_policy_rule_resource_test.go index 98865965..8c3318a3 100644 --- a/internal/provider/protocols_nfs_export_policy_rule_resource_test.go +++ b/internal/provider/protocols_nfs_export_policy_rule_resource_test.go @@ -42,6 +42,22 @@ func TestAccNFSExportPolicyRuleResource(t *testing.T) { resource.TestMatchResourceAttr("netapp-ontap_protocols_nfs_export_policy_rule_resource.example1", "id", regexp.MustCompile(`carchi-test_default_`)), ), }, + // Test importing a resource + { + ResourceName: "netapp-ontap_protocols_nfs_export_policy_rule_resource.example1", + ImportState: true, + ImportStateId: fmt.Sprintf("%s,%s,%s,%s", "1", "default", "carchi-test", "cluster4"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("netapp-ontap_protocols_nfs_export_policy_rule_resource.example1", "svm_name", "carchi-test"), + resource.TestCheckResourceAttr("netapp-ontap_protocols_nfs_export_policy_rule_resource.example1", "export_policy_name", "default"), + resource.TestCheckResourceAttr("netapp-ontap_protocols_nfs_export_policy_rule_resource.example1", "allow_suid", "true"), + resource.TestCheckTypeSetElemAttr("netapp-ontap_protocols_nfs_export_policy_rule_resource.example1", "protocols.*", "nfs3"), + resource.TestCheckTypeSetElemAttr("netapp-ontap_protocols_nfs_export_policy_rule_resource.example1", "ro_rule.*", "krb5i"), + resource.TestCheckTypeSetElemAttr("netapp-ontap_protocols_nfs_export_policy_rule_resource.example1", "rw_rule.*", "any"), + // check id + resource.TestMatchResourceAttr("netapp-ontap_protocols_nfs_export_policy_rule_resource.example1", "id", regexp.MustCompile(`carchi-test_default_`)), + ), + }, }, }) } diff --git a/internal/provider/snapmirror_policy_resource.go b/internal/provider/snapmirror_policy_resource.go index 9b31b710..683ef5c9 100644 --- a/internal/provider/snapmirror_policy_resource.go +++ b/internal/provider/snapmirror_policy_resource.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "strconv" + "strings" "github.com/hashicorp/terraform-plugin-framework-validators/boolvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" @@ -255,7 +256,12 @@ func (r *SnapmirrorPolicyResource) Read(ctx context.Context, req resource.ReadRe return } - restInfo, err := interfaces.GetSnapmirrorPolicy(errorHandler, *client, data.ID.ValueString()) + var restInfo *interfaces.SnapmirrorPolicyGetRawDataModelONTAP + if data.ID.ValueString() != "" { + restInfo, err = interfaces.GetSnapmirrorPolicy(errorHandler, *client, data.ID.ValueString()) + } else { + restInfo, err = interfaces.GetSnapmirrorPolicyByName(errorHandler, *client, data.Name.ValueString(), data.SVMName.ValueString()) + } if err != nil { // error reporting done inside GETSnapmirrorPolicy return @@ -269,10 +275,17 @@ func (r *SnapmirrorPolicyResource) Read(ctx context.Context, req resource.ReadRe if restInfo.SyncType != "" { data.SyncType = types.StringValue(restInfo.SyncType) } + if restInfo.Comment != "" { + data.Comment = types.StringValue(restInfo.Comment) + } + if restInfo.IdentityPreservation != "" { + data.IdentityPreservation = types.StringValue(restInfo.IdentityPreservation) + } data.CopyAllSourceSnapshots = types.BoolValue(restInfo.CopyAllSourceSnapshots) data.NetworkCompressionEnabled = types.BoolValue(restInfo.NetworkCompressionEnabled) data.CopyLatestSourceSnapshot = types.BoolValue(restInfo.CopyLatestSourceSnapshot) data.CreateSnapshotOnSource = types.BoolValue(restInfo.CreateSnapshotOnSource) + data.ID = types.StringValue(restInfo.UUID) // if len(restInfo.Retention) == 0 { if restInfo.Retention == nil { @@ -621,5 +634,17 @@ func (r *SnapmirrorPolicyResource) Delete(ctx context.Context, req resource.Dele // ImportState imports a resource using ID from terraform import command by calling the Read method. func (r *SnapmirrorPolicyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + idParts := strings.Split(req.ID, ",") + + if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" { + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("Expected import identifier with format: name,svm_name,cx_profile_name. Got: %q", req.ID), + ) + return + } + + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("name"), idParts[0])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("svm_name"), idParts[1])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("cx_profile_name"), idParts[2])...) } diff --git a/scripts/bd_scan.bash b/scripts/bd_scan.bash new file mode 100644 index 00000000..1fd6400e --- /dev/null +++ b/scripts/bd_scan.bash @@ -0,0 +1,39 @@ + +help_and_exit () { + echo 'This script expects a URL and API_TOKEN to connect to a BD server' + echo "eg: $0 https://blackduck.domain.com N0TAr3AlKeY@tA1L" + exit 1 +} + +# change these 2 values to reflect your project name and version: +export DETECT_PROJECT_NAME="Terraform NetApp ONTAP Provider" +export DETECT_PROJECT_VERSION_NAME=1.1.0 +export DETECT_CODE_LOCATION_NAME="${DETECT_PROJECT_NAME}_${DETECT_PROJECT_VERSION_NAME}_code" +export DETECT_BOM_AGGREGATE_NAME="${DETECT_PROJECT_NAME}_${DETECT_PROJECT_VERSION_NAME}_bom" + +# set this to true for python or yaml. false for go or other compiled language +export DETECT_DETECTOR_BUILDLESS=false + +# additionally as needed +# detect.detector.search.depth +# see https://blackducksoftware.github.io/synopsys-detect/latest/ for help + +# add go path +#export DETECT_GO_PATH="/usr/bin/go" +# add git path +#export DETECT_GIT_PATH="/usr/bin/git" +# add java path +#export DETECT_JAVA_PATH="/usr/software/java/openjdk-11.0.15_10/bin/java" + +if [ -z "$1" ]; then + help_and_exit +fi + +if [ -z "$2" ]; then + help_and_exit +fi + +export BLACKDUCK_URL=$1 +export BLACKDUCK_API_TOKEN=$2 + +bash <(curl -s -L https://detect.synopsys.com/detect7.sh) --blackduck.trust.cert=true \ No newline at end of file