Skip to content

Commit 0693ef2

Browse files
committed
Implement archive_on_destroy attribute for projects. Closes #761
This change implements an `archive_on_destroy` attribute on the `gitlab_project` resource. It defaults to `false`, if set to `true`, on a destroy it will *archive* the project instead of deleting it. This is especially useful in organizations where repositories need to be kept for regulatory purposes and can't be deleted.
1 parent a9713fa commit 0693ef2

File tree

3 files changed

+94
-30
lines changed

3 files changed

+94
-30
lines changed

docs/resources/project.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ resource "gitlab_project" "example-two" {
4444

4545
- **allow_merge_on_skipped_pipeline** (Boolean) Set to true if you want to treat skipped pipelines as if they finished with success.
4646
- **approvals_before_merge** (Number) Number of merge request approvals required for merging. Default is 0.
47+
- **archive_on_destroy** (Boolean) Set to `true` to archive the project instead of deleting on destroy. If set to `true` it will entire omit the `DELETE` operation.
4748
- **archived** (Boolean) Whether the project is in read-only mode (archived). Repositories can be archived/unarchived by toggling this parameter.
4849
- **build_coverage_regex** (String) Test coverage parsing for the project.
4950
- **ci_config_path** (String) Custom Path to CI config file.

gitlab/resource_gitlab_project.go

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,11 @@ var resourceGitLabProjectSchema = map[string]*schema.Schema{
358358
Type: schema.TypeString,
359359
Optional: true,
360360
},
361+
"archive_on_destroy": {
362+
Description: "Set to `true` to archive the project instead of deleting on destroy. If set to `true` it will entire omit the `DELETE` operation.",
363+
Type: schema.TypeBool,
364+
Optional: true,
365+
},
361366
}
362367

363368
func resourceGitlabProject() *schema.Resource {
@@ -857,43 +862,53 @@ func resourceGitlabProjectUpdate(ctx context.Context, d *schema.ResourceData, me
857862

858863
func resourceGitlabProjectDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
859864
client := meta.(*gitlab.Client)
860-
log.Printf("[DEBUG] Delete gitlab project %s", d.Id())
861865

862-
_, err := client.Projects.DeleteProject(d.Id(), gitlab.WithContext(ctx))
863-
if err != nil {
864-
return diag.FromErr(err)
865-
}
866+
if !d.Get("archive_on_destroy").(bool) {
867+
log.Printf("[DEBUG] Delete gitlab project %s", d.Id())
868+
_, err := client.Projects.DeleteProject(d.Id(), gitlab.WithContext(ctx))
869+
if err != nil {
870+
return diag.FromErr(err)
871+
}
866872

867-
// Wait for the project to be deleted.
868-
// Deleting a project in gitlab is async.
869-
stateConf := &resource.StateChangeConf{
870-
Pending: []string{"Deleting"},
871-
Target: []string{"Deleted"},
872-
Refresh: func() (interface{}, string, error) {
873-
out, response, err := client.Projects.GetProject(d.Id(), nil, gitlab.WithContext(ctx))
874-
if err != nil {
875-
if response.StatusCode == 404 {
873+
// Wait for the project to be deleted.
874+
// Deleting a project in gitlab is async.
875+
stateConf := &resource.StateChangeConf{
876+
Pending: []string{"Deleting"},
877+
Target: []string{"Deleted"},
878+
Refresh: func() (interface{}, string, error) {
879+
out, response, err := client.Projects.GetProject(d.Id(), nil, gitlab.WithContext(ctx))
880+
if err != nil {
881+
if response.StatusCode == 404 {
882+
return out, "Deleted", nil
883+
}
884+
log.Printf("[ERROR] Received error: %#v", err)
885+
return out, "Error", err
886+
}
887+
if out.MarkedForDeletionAt != nil {
888+
// Represents a Gitlab EE soft-delete
876889
return out, "Deleted", nil
877890
}
878-
log.Printf("[ERROR] Received error: %#v", err)
879-
return out, "Error", err
880-
}
881-
if out.MarkedForDeletionAt != nil {
882-
// Represents a Gitlab EE soft-delete
883-
return out, "Deleted", nil
884-
}
885-
return out, "Deleting", nil
886-
},
891+
return out, "Deleting", nil
892+
},
887893

888-
Timeout: 10 * time.Minute,
889-
MinTimeout: 3 * time.Second,
890-
Delay: 5 * time.Second,
891-
}
894+
Timeout: 10 * time.Minute,
895+
MinTimeout: 3 * time.Second,
896+
Delay: 5 * time.Second,
897+
}
892898

893-
_, err = stateConf.WaitForStateContext(ctx)
894-
if err != nil {
895-
return diag.Errorf("error waiting for project (%s) to become deleted: %s", d.Id(), err)
899+
_, err = stateConf.WaitForStateContext(ctx)
900+
if err != nil {
901+
return diag.Errorf("error waiting for project (%s) to become deleted: %s", d.Id(), err)
902+
}
903+
904+
} else {
905+
log.Printf("[DEBUG] Archive gitlab project %s", d.Id())
906+
_, _, err := client.Projects.ArchiveProject(d.Id(), gitlab.WithContext(ctx))
907+
if err != nil {
908+
return diag.FromErr(err)
909+
}
896910
}
911+
897912
return nil
898913
}
899914

gitlab/resource_gitlab_project_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,21 @@ func TestAccGitlabProject_initializeWithoutReadme(t *testing.T) {
383383
})
384384
}
385385

386+
func TestAccGitlabProject_archiveOnDestroy(t *testing.T) {
387+
rInt := acctest.RandInt()
388+
389+
resource.Test(t, resource.TestCase{
390+
PreCheck: func() { testAccPreCheck(t) },
391+
Providers: testAccProviders,
392+
CheckDestroy: testAccCheckGitlabProjectArchivedOnDestroy,
393+
Steps: []resource.TestStep{
394+
{
395+
Config: testAccGitlabProjectConfigArchiveOnDestroy(rInt),
396+
},
397+
},
398+
})
399+
}
400+
386401
func TestAccGitlabProject_IssueMergeRequestTemplates(t *testing.T) {
387402
var project gitlab.Project
388403
rInt := acctest.RandInt()
@@ -881,6 +896,27 @@ func testAccCheckGitlabProjectDestroy(s *terraform.State) error {
881896
return nil
882897
}
883898

899+
func testAccCheckGitlabProjectArchivedOnDestroy(s *terraform.State) error {
900+
conn := testAccProvider.Meta().(*gitlab.Client)
901+
for _, rs := range s.RootModule().Resources {
902+
if rs.Type != "gitlab_project" {
903+
continue
904+
}
905+
906+
gotRepo, _, err := conn.Projects.GetProject(rs.Primary.ID, nil)
907+
if err != nil {
908+
return fmt.Errorf("unable to get project %s, to check if it has been archived on the destroy", rs.Primary.ID)
909+
}
910+
911+
if !gotRepo.Archived {
912+
return fmt.Errorf("expected project to be archived, but it isn't")
913+
}
914+
return nil
915+
}
916+
917+
return fmt.Errorf("no project resources found in state, but expected a `gitlab_project` resource marked as archvied")
918+
}
919+
884920
func testAccCheckAggregateGitlabProject(expected, received *gitlab.Project) resource.TestCheckFunc {
885921
var checks []resource.TestCheckFunc
886922

@@ -1343,3 +1379,15 @@ resource "gitlab_project" "foo" {
13431379
}
13441380
`, rInt, rInt)
13451381
}
1382+
1383+
func testAccGitlabProjectConfigArchiveOnDestroy(rInt int) string {
1384+
return fmt.Sprintf(`
1385+
resource "gitlab_project" "foo" {
1386+
name = "foo-%d"
1387+
path = "foo.%d"
1388+
description = "Terraform acceptance tests"
1389+
archive_on_destroy = true
1390+
archived = false
1391+
}
1392+
`, rInt, rInt)
1393+
}

0 commit comments

Comments
 (0)