Skip to content

Commit df1ea28

Browse files
authored
Merge pull request #246 from NetApp/236-bug-unable-to-set-cifs-acl-1
fix set acl error.
2 parents 1b4d0e1 + 9a8b434 commit df1ea28

File tree

5 files changed

+376
-16
lines changed

5 files changed

+376
-16
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
## 1.1.3
22

33
BUG FIXES:
4-
54
* **netapp-ontap_protocols_cifs_service_resource**: fixed on attribute checking ([#250](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/250))
5+
* **netapp-ontap_protocols_cifs_share_resource** :`acls` unable to update acls ([#236](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/236))
66
* **netapp-ontap_protocols_san_igroups_resource**: fixed bug nil pointer dereference ([#247](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/247))
77

88

internal/interfaces/protocols_cifs_share.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,20 @@ type ProtocolsCIFSShareResourceBodyDataModelONTAP struct {
5252
Name string `mapstructure:"name,omitempty"` // can't be present in update, so omit empty.
5353
SVM svm `mapstructure:"svm"`
5454
Acls []Acls `mapstructure:"acls,omitempty"` // API complains if this is not omit empty
55-
ChangeNotify bool `mapstructure:"change_notify"`
55+
ChangeNotify bool `mapstructure:"change_notify,omitempty"`
5656
Comment string `mapstructure:"comment,omitempty"` // API complains if this is not omit empty
57-
ContinuouslyAvailable bool `mapstructure:"continuously_available"`
58-
DirUmask int64 `mapstructure:"dir_umask"`
59-
Encryption bool `mapstructure:"encryption"`
60-
FileUmask int64 `mapstructure:"file_umask"`
61-
ForceGroupForCreate string `mapstructure:"force_group_for_create"`
57+
ContinuouslyAvailable bool `mapstructure:"continuously_available,omitempty"`
58+
DirUmask int64 `mapstructure:"dir_umask,omitempty"`
59+
Encryption bool `mapstructure:"encryption,omitempty"`
60+
FileUmask int64 `mapstructure:"file_umask,omitempty"`
61+
ForceGroupForCreate string `mapstructure:"force_group_for_create,omitempty"`
6262
HomeDirectory bool `mapstructure:"home_directory,omitempty"` // can't be present in update, so omit empty.
63-
NamespaceCaching bool `mapstructure:"namespace_caching"`
64-
NoStrictSecurity bool `mapstructure:"no_strict_security"`
63+
NamespaceCaching bool `mapstructure:"namespace_caching,omitempty"`
64+
NoStrictSecurity bool `mapstructure:"no_strict_security,omitempty"`
6565
OfflineFiles string `mapstructure:"offline_files,omitempty"` // API complains if this is not omit empty
66-
Oplocks bool `mapstructure:"oplocks"`
66+
Oplocks bool `mapstructure:"oplocks,omitempty"`
6767
Path string `mapstructure:"path,omitempty"` // can't be present in update, so omit empty.
68-
ShowSnapshot bool `mapstructure:"show_snapshot"`
68+
ShowSnapshot bool `mapstructure:"show_snapshot,omitempty"`
6969
UnixSymlink string `mapstructure:"unix_symlink,omitempty"` // API complains if this is not omit empty
7070
VscanProfile string `mapstructure:"vscan_profile,omitempty"` // API complains if this is not omit empty
7171
}
@@ -159,14 +159,14 @@ func CreateProtocolsCIFSShare(errorHandler *utils.ErrorHandler, r restclient.Res
159159

160160
// UpdateProtocolsCIFSShare to update protocols_cifs_share
161161
func UpdateProtocolsCIFSShare(errorHandler *utils.ErrorHandler, r restclient.RestClient, body ProtocolsCIFSShareResourceBodyDataModelONTAP, name string, svmUUID string) error {
162-
api := "/protocols/cifs/shares/"
162+
api := fmt.Sprintf("/protocols/cifs/shares/%s/%s", svmUUID, name)
163163
var bodyMap map[string]interface{}
164164
if err := mapstructure.Decode(body, &bodyMap); err != nil {
165165
return errorHandler.MakeAndReportError("error encoding protocols_cifs_share body", fmt.Sprintf("error on encoding %s body: %s, body: %#v", api, err, body))
166166
}
167-
statusCode, _, err := r.CallUpdateMethod(api+"/"+svmUUID+"/"+name, nil, bodyMap)
167+
statusCode, _, err := r.CallUpdateMethod(api, nil, bodyMap)
168168
if err != nil {
169-
return errorHandler.MakeAndReportError("error updating protocols_cifs_share", fmt.Sprintf("error on POST %s: %s, statusCode %d", api, err, statusCode))
169+
return errorHandler.MakeAndReportError("error updating protocols_cifs_share", fmt.Sprintf("error on PATCH %s: %s, statusCode %d", api, err, statusCode))
170170
}
171171
return nil
172172
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package interfaces
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/hashicorp/terraform-plugin-log/tflog"
7+
"github.com/mitchellh/mapstructure"
8+
"github.com/netapp/terraform-provider-netapp-ontap/internal/restclient"
9+
"github.com/netapp/terraform-provider-netapp-ontap/internal/utils"
10+
)
11+
12+
// ProtocolsCIFSShareACLGetDataModelONTAP describes the GET record data model using go types for mapping.
13+
type ProtocolsCIFSShareACLGetDataModelONTAP struct {
14+
Name string `mapstructure:"name"`
15+
UUID string `mapstructure:"uuid"`
16+
UserOrGroup string `mapstructure:"user_or_group"`
17+
}
18+
19+
// ProtocolsCIFSShareACLResourceBodyDataModelONTAP describes the body data model using go types for mapping.
20+
type ProtocolsCIFSShareACLResourceBodyDataModelONTAP struct {
21+
// Name string `mapstructure:"name"`
22+
// SVM svm `mapstructure:"svm"`
23+
Permission string `mapstructure:"permission"`
24+
UserOrGroup string `mapstructure:"user_or_group"`
25+
Type string `mapstructure:"type"`
26+
}
27+
28+
// ProtocolsCIFSShareACLDataSourceFilterModel describes the data source data model for queries.
29+
type ProtocolsCIFSShareACLDataSourceFilterModel struct {
30+
Name string `mapstructure:"name"`
31+
SVMName string `mapstructure:"svm.name"`
32+
UserOrGroup string `mapstructure:"user_or_group"`
33+
}
34+
35+
// GetProtocolsCIFSShareACLByName to get protocols_cifs_share_acl info
36+
func GetProtocolsCIFSShareACLByName(errorHandler *utils.ErrorHandler, r restclient.RestClient, name string, svmName string) (*ProtocolsCIFSShareACLGetDataModelONTAP, error) {
37+
api := "api_url"
38+
query := r.NewQuery()
39+
query.Set("name", name)
40+
if svmName == "" {
41+
query.Set("scope", "cluster")
42+
} else {
43+
query.Set("svm.name", svmName)
44+
query.Set("scope", "svm")
45+
}
46+
query.Fields([]string{"name", "svm.name", "ip", "scope"})
47+
statusCode, response, err := r.GetNilOrOneRecord(api, query, nil)
48+
if err == nil && response == nil {
49+
err = fmt.Errorf("no response for GET %s", api)
50+
}
51+
if err != nil {
52+
return nil, errorHandler.MakeAndReportError("error reading protocols_cifs_share_acl info", fmt.Sprintf("error on GET %s: %s, statusCode %d", api, err, statusCode))
53+
}
54+
55+
var dataONTAP ProtocolsCIFSShareACLGetDataModelONTAP
56+
if err := mapstructure.Decode(response, &dataONTAP); err != nil {
57+
return nil, errorHandler.MakeAndReportError(fmt.Sprintf("failed to decode response from GET %s", api),
58+
fmt.Sprintf("error: %s, statusCode %d, response %#v", err, statusCode, response))
59+
}
60+
tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Read protocols_cifs_share_acl data source: %#v", dataONTAP))
61+
return &dataONTAP, nil
62+
}
63+
64+
// GetProtocolsCIFSShareAcls to get protocols_cifs_share_acl info for all resources matching a filter
65+
func GetProtocolsCIFSShareAcls(errorHandler *utils.ErrorHandler, r restclient.RestClient, filter *ProtocolsCIFSShareACLDataSourceFilterModel, svmName string, shareName string) ([]ProtocolsCIFSShareACLGetDataModelONTAP, error) {
66+
api := fmt.Sprintf("/protocols/cifs/shares/%s/%s/acls", svmName, shareName)
67+
query := r.NewQuery()
68+
query.Fields([]string{"name", "svm.name", "scope"})
69+
if filter != nil {
70+
var filterMap map[string]interface{}
71+
if err := mapstructure.Decode(filter, &filterMap); err != nil {
72+
return nil, errorHandler.MakeAndReportError("error encoding protocols_cifs_share_acls filter info", fmt.Sprintf("error on filter %#v: %s", filter, err))
73+
}
74+
query.SetValues(filterMap)
75+
}
76+
statusCode, response, err := r.GetZeroOrMoreRecords(api, query, nil)
77+
if err == nil && response == nil {
78+
err = fmt.Errorf("no response for GET %s", api)
79+
}
80+
if err != nil {
81+
return nil, errorHandler.MakeAndReportError("error reading protocols_cifs_share_acls info", fmt.Sprintf("error on GET %s: %s, statusCode %d", api, err, statusCode))
82+
}
83+
84+
var dataONTAP []ProtocolsCIFSShareACLGetDataModelONTAP
85+
for _, info := range response {
86+
var record ProtocolsCIFSShareACLGetDataModelONTAP
87+
if err := mapstructure.Decode(info, &record); err != nil {
88+
return nil, errorHandler.MakeAndReportError(fmt.Sprintf("failed to decode response from GET %s", api),
89+
fmt.Sprintf("error: %s, statusCode %d, info %#v", err, statusCode, info))
90+
}
91+
dataONTAP = append(dataONTAP, record)
92+
}
93+
tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Read protocols_cifs_share_acls data source: %#v", dataONTAP))
94+
return dataONTAP, nil
95+
}
96+
97+
// CreateProtocolsCIFSShareACL to create protocols_cifs_share_acl
98+
func CreateProtocolsCIFSShareACL(errorHandler *utils.ErrorHandler, r restclient.RestClient, body ProtocolsCIFSShareACLResourceBodyDataModelONTAP, svmID string, shareName string) (*ProtocolsCIFSShareACLGetDataModelONTAP, error) {
99+
api := fmt.Sprintf("/protocols/cifs/shares/%s/%s/acls", svmID, shareName)
100+
var bodyMap map[string]interface{}
101+
if err := mapstructure.Decode(body, &bodyMap); err != nil {
102+
return nil, errorHandler.MakeAndReportError("error encoding protocols_cifs_share_acl body", fmt.Sprintf("error on encoding %s body: %s, body: %#v", api, err, body))
103+
}
104+
query := r.NewQuery()
105+
query.Add("return_records", "true")
106+
statusCode, response, err := r.CallCreateMethod(api, query, bodyMap)
107+
if err != nil {
108+
return nil, errorHandler.MakeAndReportError("error creating protocols_cifs_share_acl", fmt.Sprintf("error on POST %s: %s, statusCode %d", api, err, statusCode))
109+
}
110+
111+
var dataONTAP ProtocolsCIFSShareACLGetDataModelONTAP
112+
if err := mapstructure.Decode(response.Records[0], &dataONTAP); err != nil {
113+
return nil, errorHandler.MakeAndReportError("error decoding protocols_cifs_share_acl info", fmt.Sprintf("error on decode storage/protocols_cifs_share_acls info: %s, statusCode %d, response %#v", err, statusCode, response))
114+
}
115+
tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Create protocols_cifs_share_acl source - udata: %#v", dataONTAP))
116+
return &dataONTAP, nil
117+
}
118+
119+
// UpdateProtocolsCIFSShareACL to update protocols_cifs_share_acl
120+
func UpdateProtocolsCIFSShareACL(errorHandler *utils.ErrorHandler, r restclient.RestClient, body ProtocolsCIFSShareACLResourceBodyDataModelONTAP, svmID string, shareName string, userOrGroup string, aclType string) error {
121+
api := fmt.Sprintf("/protocols/cifs/shares/%s/%s/acls/%s/%s", svmID, shareName, userOrGroup, aclType)
122+
var bodyMap map[string]interface{}
123+
if err := mapstructure.Decode(body, &bodyMap); err != nil {
124+
return errorHandler.MakeAndReportError("error encoding protocols_cifs_share_acl body", fmt.Sprintf("error on encoding %s body: %s, body: %#v", api, err, body))
125+
}
126+
delete(bodyMap, "type") // type is not returned in the response
127+
delete(bodyMap, "user_or_group") // user_or_group is not returned in the response
128+
statusCode, _, err := r.CallUpdateMethod(api, nil, bodyMap)
129+
if err != nil {
130+
return errorHandler.MakeAndReportError("error updating protocols_cifs_share_acl", fmt.Sprintf("error on PATCH %s: %s, statusCode %d", api, err, statusCode))
131+
}
132+
return nil
133+
}
134+
135+
// DeleteProtocolsCIFSShareACL to delete protocols_cifs_share_acl
136+
func DeleteProtocolsCIFSShareACL(errorHandler *utils.ErrorHandler, r restclient.RestClient, svmID string, shareName string, userOrGroup string, aclType string) error {
137+
api := fmt.Sprintf("/protocols/cifs/shares/%s/%s/acls/%s/%s", svmID, shareName, userOrGroup, aclType)
138+
statusCode, _, err := r.CallDeleteMethod(api, nil, nil)
139+
if err != nil {
140+
return errorHandler.MakeAndReportError("error deleting protocols_cifs_share_acl", fmt.Sprintf("error on DELETE %s: %s, statusCode %d", api, err, statusCode))
141+
}
142+
return nil
143+
}

internal/provider/protocols_cifs_share_resource.go

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ func (r *ProtocolsCIFSShareResource) Create(ctx context.Context, req resource.Cr
432432
body.SVM.Name = data.SVMName.ValueString()
433433
body.Path = data.Path.ValueString()
434434

435+
configHasDefaultACL := false
435436
if !data.Acls.IsUnknown() {
436437
aclsList := []interfaces.Acls{}
437438
elements := make([]types.Object, 0, len(data.Acls.Elements()))
@@ -451,7 +452,9 @@ func (r *ProtocolsCIFSShareResource) Create(ctx context.Context, req resource.Cr
451452
interfacesAcls.Permission = acls.Permission
452453
interfacesAcls.Type = acls.Type
453454
interfacesAcls.UserOrGroup = acls.UserOrGroup
454-
455+
if acls.UserOrGroup == "Everyone" && acls.Permission == "full_control" {
456+
configHasDefaultACL = true
457+
}
455458
aclsList = append(aclsList, interfacesAcls)
456459
}
457460
body.Acls = aclsList
@@ -553,6 +556,20 @@ func (r *ProtocolsCIFSShareResource) Create(ctx context.Context, req resource.Cr
553556
data.Acls = setValue
554557
} else {
555558
for _, acls := range restInfo.Acls {
559+
//If the config file does not have acl set user_or_group as "Everyone / Full Control", the API will create one by default. Need to delete it if user does not want one.
560+
if acls.UserOrGroup == "Everyone" && acls.Permission == "full_control" && !configHasDefaultACL {
561+
svm, err := interfaces.GetSvmByName(errorHandler, *client, data.SVMName.ValueString())
562+
if err != nil {
563+
return
564+
}
565+
err = interfaces.DeleteProtocolsCIFSShareACL(errorHandler, *client, svm.UUID, data.Name.ValueString(), acls.UserOrGroup, acls.Type)
566+
if err != nil {
567+
// error reporting done inside DeleteProtocolsCIFSShareAcl
568+
return
569+
}
570+
continue
571+
}
572+
556573
elementType := map[string]attr.Type{
557574
"permission": types.StringType,
558575
"type": types.StringType,
@@ -691,11 +708,115 @@ func (r *ProtocolsCIFSShareResource) Update(ctx context.Context, req resource.Up
691708
}
692709
}
693710

711+
if !plan.ContinuouslyAvailable.IsUnknown() {
712+
if plan.ContinuouslyAvailable != state.ContinuouslyAvailable {
713+
body.ContinuouslyAvailable = plan.ContinuouslyAvailable.ValueBool()
714+
}
715+
}
716+
694717
svm, err := interfaces.GetSvmByName(errorHandler, *client, plan.SVMName.ValueString())
695718
if err != nil {
696719
return
697720
}
698721

722+
// have no luck updating acls using PATCH cifs/shares API sucessfully, so we have to use the acls set of API.
723+
if !plan.Acls.IsUnknown() {
724+
// reading acls from plan
725+
planeAcls := make([]types.Object, 0, len(plan.Acls.Elements()))
726+
diags := plan.Acls.ElementsAs(ctx, &planeAcls, false)
727+
if diags.HasError() {
728+
resp.Diagnostics.Append(diags...)
729+
return
730+
}
731+
// reading acls from state
732+
stateAcls := make([]types.Object, 0, len(state.Acls.Elements()))
733+
diags = state.Acls.ElementsAs(ctx, &stateAcls, false)
734+
if diags.HasError() {
735+
resp.Diagnostics.Append(diags...)
736+
return
737+
}
738+
// iterate over plan acls and compare with state acls. Make create or update or delete calls accordingly.
739+
for _, element := range stateAcls {
740+
var stateACLElement ProtocolsCIFSShareResourceAcls
741+
diags := element.As(ctx, &stateACLElement, basetypes.ObjectAsOptions{})
742+
if diags.HasError() {
743+
resp.Diagnostics.Append(diags...)
744+
return
745+
}
746+
for index, planACL := range planeAcls {
747+
var planACLElement ProtocolsCIFSShareResourceAcls
748+
diags := planACL.As(ctx, &planACLElement, basetypes.ObjectAsOptions{})
749+
if diags.HasError() {
750+
resp.Diagnostics.Append(diags...)
751+
return
752+
}
753+
// if 'userOrGroup' and 'type' matches, then we know it's not a create action. If permission is same, then break the loop because nothing to update.
754+
if stateACLElement.UserOrGroup == planACLElement.UserOrGroup && stateACLElement.Type == planACLElement.Type {
755+
if stateACLElement.Permission == planACLElement.Permission {
756+
break
757+
} else {
758+
// update the acls since permission is different
759+
interfacesAcls := interfaces.ProtocolsCIFSShareACLResourceBodyDataModelONTAP{}
760+
interfacesAcls.Permission = planACLElement.Permission
761+
err = interfaces.UpdateProtocolsCIFSShareACL(errorHandler, *client, interfacesAcls, svm.UUID, plan.Name.ValueString(), planACLElement.UserOrGroup, planACLElement.Type)
762+
if err != nil {
763+
return
764+
}
765+
break
766+
}
767+
}
768+
// if we reach the end of stateAcls, then we know it's a delete action because it was not found in plan acls.
769+
if index == len(planeAcls)-1 {
770+
err = interfaces.DeleteProtocolsCIFSShareACL(errorHandler, *client, svm.UUID, plan.Name.ValueString(), stateACLElement.UserOrGroup, stateACLElement.Type)
771+
if err != nil {
772+
return
773+
}
774+
775+
}
776+
}
777+
778+
}
779+
// now handle create action
780+
for _, planACL := range planeAcls {
781+
var planACLElement ProtocolsCIFSShareResourceAcls
782+
diags := planACL.As(ctx, &planACLElement, basetypes.ObjectAsOptions{})
783+
if diags.HasError() {
784+
resp.Diagnostics.Append(diags...)
785+
return
786+
}
787+
788+
for index, element := range stateAcls {
789+
var stateACLElement ProtocolsCIFSShareResourceAcls
790+
diags := element.As(ctx, &stateACLElement, basetypes.ObjectAsOptions{})
791+
if diags.HasError() {
792+
resp.Diagnostics.Append(diags...)
793+
return
794+
}
795+
if stateACLElement.UserOrGroup == planACLElement.UserOrGroup && stateACLElement.Type == planACLElement.Type {
796+
if stateACLElement.Permission == planACLElement.Permission {
797+
break
798+
} else {
799+
// update is already handled by above logic, so break
800+
break
801+
}
802+
}
803+
// if we reach the end of planAcls, then we know it's a create action because it was not found in state acls.
804+
if index == len(stateAcls)-1 {
805+
interfacesAcls := interfaces.ProtocolsCIFSShareACLResourceBodyDataModelONTAP{}
806+
interfacesAcls.Permission = planACLElement.Permission
807+
interfacesAcls.Type = planACLElement.Type
808+
interfacesAcls.UserOrGroup = planACLElement.UserOrGroup
809+
_, err = interfaces.CreateProtocolsCIFSShareACL(errorHandler, *client, interfacesAcls, svm.UUID, plan.Name.ValueString())
810+
if err != nil {
811+
return
812+
}
813+
}
814+
}
815+
816+
}
817+
818+
}
819+
699820
err = interfaces.UpdateProtocolsCIFSShare(errorHandler, *client, body, plan.Name.ValueString(), svm.UUID)
700821
if err != nil {
701822
return

0 commit comments

Comments
 (0)