@@ -93,6 +93,28 @@ func resourceGitlabProject() *schema.Resource {
93
93
Type : schema .TypeString ,
94
94
Computed : true ,
95
95
},
96
+ "shared_with_groups" : {
97
+ Type : schema .TypeList ,
98
+ Optional : true ,
99
+ Elem : & schema.Resource {
100
+ Schema : map [string ]* schema.Schema {
101
+ "group_id" : {
102
+ Type : schema .TypeInt ,
103
+ Required : true ,
104
+ },
105
+ "group_access_level" : {
106
+ Type : schema .TypeString ,
107
+ Required : true ,
108
+ ValidateFunc : validation .StringInSlice ([]string {
109
+ "guest" , "reporter" , "developer" , "master" }, false ),
110
+ },
111
+ "group_name" : {
112
+ Type : schema .TypeString ,
113
+ Computed : true ,
114
+ },
115
+ },
116
+ },
117
+ },
96
118
},
97
119
}
98
120
}
@@ -114,6 +136,7 @@ func resourceGitlabProjectSetToState(d *schema.ResourceData, project *gitlab.Pro
114
136
d .Set ("http_url_to_repo" , project .HTTPURLToRepo )
115
137
d .Set ("web_url" , project .WebURL )
116
138
d .Set ("runners_token" , project .RunnersToken )
139
+ d .Set ("shared_with_groups" , flattenSharedWithGroupsOptions (project ))
117
140
}
118
141
119
142
func resourceGitlabProjectCreate (d * schema.ResourceData , meta interface {}) error {
@@ -146,6 +169,18 @@ func resourceGitlabProjectCreate(d *schema.ResourceData, meta interface{}) error
146
169
return err
147
170
}
148
171
172
+ if v , ok := d .GetOk ("shared_with_groups" ); ok {
173
+ options := expandSharedWithGroupsOptions (v .([]interface {}))
174
+
175
+ for _ , option := range options {
176
+ log .Printf ("[DEBUG] project shared with group %v" , option )
177
+ _ , err := client .Projects .ShareProjectWithGroup (project .ID , option )
178
+ if err != nil {
179
+ return err
180
+ }
181
+ }
182
+ }
183
+
149
184
d .SetId (fmt .Sprintf ("%d" , project .ID ))
150
185
151
186
return resourceGitlabProjectRead (d , meta )
@@ -211,11 +246,16 @@ func resourceGitlabProjectUpdate(d *schema.ResourceData, meta interface{}) error
211
246
options .SnippetsEnabled = gitlab .Bool (d .Get ("snippets_enabled" ).(bool ))
212
247
}
213
248
214
- log .Printf ("[DEBUG] update gitlab project %s" , d .Id ())
249
+ if * options != (gitlab.EditProjectOptions {}) {
250
+ log .Printf ("[DEBUG] update gitlab project %s" , d .Id ())
251
+ _ , _ , err := client .Projects .EditProject (d .Id (), options )
252
+ if err != nil {
253
+ return err
254
+ }
255
+ }
215
256
216
- _ , _ , err := client .Projects .EditProject (d .Id (), options )
217
- if err != nil {
218
- return err
257
+ if d .HasChange ("shared_with_groups" ) {
258
+ updateSharedWithGroups (d , meta )
219
259
}
220
260
221
261
return resourceGitlabProjectRead (d , meta )
@@ -258,3 +298,124 @@ func resourceGitlabProjectDelete(d *schema.ResourceData, meta interface{}) error
258
298
}
259
299
return nil
260
300
}
301
+
302
+ func expandSharedWithGroupsOptions (d []interface {}) []* gitlab.ShareWithGroupOptions {
303
+ shareWithGroupOptionsList := []* gitlab.ShareWithGroupOptions {}
304
+
305
+ for _ , config := range d {
306
+ data := config .(map [string ]interface {})
307
+
308
+ groupAccess := accessLevelNameToValue [data ["group_access_level" ].(string )]
309
+
310
+ shareWithGroupOptions := & gitlab.ShareWithGroupOptions {
311
+ GroupID : gitlab .Int (data ["group_id" ].(int )),
312
+ GroupAccess : & groupAccess ,
313
+ }
314
+
315
+ shareWithGroupOptionsList = append (shareWithGroupOptionsList ,
316
+ shareWithGroupOptions )
317
+ }
318
+
319
+ return shareWithGroupOptionsList
320
+ }
321
+
322
+ func flattenSharedWithGroupsOptions (project * gitlab.Project ) []interface {} {
323
+ sharedWithGroups := project .SharedWithGroups
324
+ sharedWithGroupsList := []interface {}{}
325
+
326
+ for _ , option := range sharedWithGroups {
327
+ values := map [string ]interface {}{
328
+ "group_id" : option .GroupID ,
329
+ "group_access_level" : accessLevelValueToName [gitlab .AccessLevelValue (
330
+ option .GroupAccessLevel )],
331
+ "group_name" : option .GroupName ,
332
+ }
333
+
334
+ sharedWithGroupsList = append (sharedWithGroupsList , values )
335
+ }
336
+
337
+ return sharedWithGroupsList
338
+ }
339
+
340
+ func findGroupProjectSharedWith (target * gitlab.ShareWithGroupOptions ,
341
+ groups []* gitlab.ShareWithGroupOptions ) (* gitlab.ShareWithGroupOptions , int , error ) {
342
+ for i , group := range groups {
343
+ if * group .GroupID == * target .GroupID {
344
+ return group , i , nil
345
+ }
346
+ }
347
+
348
+ return nil , 0 , fmt .Errorf ("group not found" )
349
+ }
350
+
351
+ func getGroupsProjectSharedWith (project * gitlab.Project ) []* gitlab.ShareWithGroupOptions {
352
+ sharedGroups := []* gitlab.ShareWithGroupOptions {}
353
+
354
+ for _ , group := range project .SharedWithGroups {
355
+ sharedGroups = append (sharedGroups , & gitlab.ShareWithGroupOptions {
356
+ GroupID : gitlab .Int (group .GroupID ),
357
+ GroupAccess : gitlab .AccessLevel (gitlab .AccessLevelValue (
358
+ group .GroupAccessLevel )),
359
+ })
360
+ }
361
+
362
+ return sharedGroups
363
+ }
364
+
365
+ func updateSharedWithGroups (d * schema.ResourceData , meta interface {}) error {
366
+ client := meta .(* gitlab.Client )
367
+
368
+ var groupsToUnshare []* gitlab.ShareWithGroupOptions
369
+ var groupsToShare []* gitlab.ShareWithGroupOptions
370
+
371
+ // Get target groups from the TF config and current groups from Gitlab server
372
+ targetGroups := expandSharedWithGroupsOptions (
373
+ d .Get ("shared_with_groups" ).([]interface {}))
374
+ project , _ , err := client .Projects .GetProject (d .Id ())
375
+ if err != nil {
376
+ return err
377
+ }
378
+ currentGroups := getGroupsProjectSharedWith (project )
379
+
380
+ for _ , targetGroup := range targetGroups {
381
+ currentGroup , index , err := findGroupProjectSharedWith (targetGroup , currentGroups )
382
+
383
+ // If no corresponding group is found, it must be added
384
+ if err != nil {
385
+ groupsToShare = append (groupsToShare , targetGroup )
386
+ continue
387
+ }
388
+
389
+ // If group is different it must be deleted and added again
390
+ if * targetGroup .GroupAccess != * currentGroup .GroupAccess {
391
+ groupsToShare = append (groupsToShare , targetGroup )
392
+ groupsToUnshare = append (groupsToUnshare , targetGroup )
393
+ }
394
+
395
+ // Remove currentGroup from from list
396
+ currentGroups = append (currentGroups [:index ], currentGroups [index + 1 :]... )
397
+ }
398
+
399
+ // All groups still present in currentGroup must be deleted
400
+ groupsToUnshare = append (groupsToUnshare , currentGroups ... )
401
+
402
+ // Unshare groups to delete and update
403
+ for _ , group := range groupsToUnshare {
404
+ log .Printf ("[DEBUG] update shared_with_group: unshare %d" , * group .GroupID )
405
+ _ , err := client .Projects .DeleteSharedProjectFromGroup (d .Id (), * group .GroupID )
406
+ if err != nil {
407
+ return err
408
+ }
409
+ }
410
+
411
+ // Share groups to add and update
412
+ for _ , group := range groupsToShare {
413
+ log .Printf ("[DEBUG] update shared_with_group: share %d" , * group .GroupID )
414
+ _ , err := client .Projects .ShareProjectWithGroup (d .Id (), group )
415
+ if err != nil {
416
+ return err
417
+ }
418
+ }
419
+
420
+ return nil
421
+ }
0 commit comments