Skip to content

Commit 78f3946

Browse files
authored
Merge pull request #967 from timofurrer/feature/delete-topic
resource/gitlab_topic: support proper deletion of topics
2 parents d18aee4 + 47298a7 commit 78f3946

File tree

3 files changed

+128
-27
lines changed

3 files changed

+128
-27
lines changed

docs/resources/topic.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,18 @@ page_title: "gitlab_topic Resource - terraform-provider-gitlab"
44
subcategory: ""
55
description: |-
66
The gitlab_topic resource allows to manage the lifecycle of topics that are then assignable to projects.
7-
Topics are the successors for project tags. Aside from avoiding terminology collisions with Git tags, they are more descriptive and better searchable.
8-
~> Deleting a resource doesn't delete the corresponding topic as the GitLab API doesn't support deleting topics yet. You can set soft_destroy to true if you want the topics description to be emptied instead.
7+
-> Topics are the successors for project tags. Aside from avoiding terminology collisions with Git tags, they are more descriptive and better searchable.
8+
~> Deleting a topic was implemented in GitLab 14.9. For older versions of GitLab set soft_destroy = true to empty out a topic instead of deleting it.
99
Upstream API: GitLab REST API docs for topics https://docs.gitlab.com/ee/api/topics.html
1010
---
1111

1212
# gitlab_topic (Resource)
1313

1414
The `gitlab_topic` resource allows to manage the lifecycle of topics that are then assignable to projects.
1515

16-
Topics are the successors for project tags. Aside from avoiding terminology collisions with Git tags, they are more descriptive and better searchable.
16+
-> Topics are the successors for project tags. Aside from avoiding terminology collisions with Git tags, they are more descriptive and better searchable.
1717

18-
~> Deleting a resource doesn't delete the corresponding topic as the GitLab API doesn't support deleting topics yet. You can set soft_destroy to true if you want the topics description to be emptied instead.
18+
~> Deleting a topic was implemented in GitLab 14.9. For older versions of GitLab set `soft_destroy = true` to empty out a topic instead of deleting it.
1919

2020
**Upstream API**: [GitLab REST API docs for topics](https://docs.gitlab.com/ee/api/topics.html)
2121

@@ -34,12 +34,12 @@ resource "gitlab_topic" "functional_programming" {
3434
### Required
3535

3636
- `name` (String) The topic's name
37-
- `soft_destroy` (Boolean) Empty the topics fields instead of deleting it
3837

3938
### Optional
4039

4140
- `description` (String) A text describing the topic
4241
- `id` (String) The ID of this resource.
42+
- `soft_destroy` (Boolean, Deprecated) Empty the topics fields instead of deleting it
4343

4444
## Import
4545

internal/provider/resource_gitlab_topic.go

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ var _ = registerResource("gitlab_topic", func() *schema.Resource {
1515
return &schema.Resource{
1616
Description: `The ` + "`gitlab_topic`" + ` resource allows to manage the lifecycle of topics that are then assignable to projects.
1717
18-
Topics are the successors for project tags. Aside from avoiding terminology collisions with Git tags, they are more descriptive and better searchable.
18+
-> Topics are the successors for project tags. Aside from avoiding terminology collisions with Git tags, they are more descriptive and better searchable.
1919
20-
~> Deleting a resource doesn't delete the corresponding topic as the GitLab API doesn't support deleting topics yet. You can set soft_destroy to true if you want the topics description to be emptied instead.
20+
~> Deleting a topic was implemented in GitLab 14.9. For older versions of GitLab set ` + "`soft_destroy = true`" + ` to empty out a topic instead of deleting it.
2121
2222
**Upstream API**: [GitLab REST API docs for topics](https://docs.gitlab.com/ee/api/topics.html)
2323
`,
@@ -39,7 +39,8 @@ Topics are the successors for project tags. Aside from avoiding terminology coll
3939
"soft_destroy": {
4040
Description: "Empty the topics fields instead of deleting it",
4141
Type: schema.TypeBool,
42-
Required: true,
42+
Optional: true,
43+
Deprecated: "GitLab 14.9 introduced the proper deletion of topics. This field is no longer needed.",
4344
},
4445
"description": {
4546
Description: "A text describing the topic",
@@ -125,27 +126,43 @@ func resourceGitlabTopicUpdate(ctx context.Context, d *schema.ResourceData, meta
125126
}
126127

127128
func resourceGitlabTopicDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
128-
129+
client := meta.(*gitlab.Client)
130+
topicID, err := strconv.Atoi(d.Id())
131+
if err != nil {
132+
return diag.Errorf("Failed to convert topic id %s to int: %s", d.Id(), err)
133+
}
129134
softDestroy := d.Get("soft_destroy").(bool)
130135

131-
if !softDestroy {
132-
return diag.Errorf("Destroying a topic is not yet supported. You can set soft_destroy=true to suppress this error")
136+
deleteNotSupported, err := isGitLabVersionLessThan(client, "14.9")()
137+
if err != nil {
138+
return diag.FromErr(err)
139+
}
140+
if !softDestroy && deleteNotSupported {
141+
return diag.Errorf("GitLab 14.9 introduced the proper deletion of topics. Set `soft_destroy = true` to empty out a topic instead of deleting it.")
133142
}
134143

135-
log.Printf("[WARN] Not deleting gitlab topic %s. Instead emptying its description", d.Id())
144+
// NOTE: the `soft_destroy` field is deprecated and will be removed in a future version.
145+
// It was only introduced because GitLab prior to 14.9 didn't support topic deletion.
146+
if softDestroy {
147+
log.Printf("[WARN] Not deleting gitlab topic %s. Instead emptying its description", d.Id())
136148

137-
client := meta.(*gitlab.Client)
138-
options := &gitlab.UpdateTopicOptions{
139-
Description: gitlab.String(""),
140-
}
149+
options := &gitlab.UpdateTopicOptions{
150+
Description: gitlab.String(""),
151+
}
141152

142-
topicID, err := strconv.Atoi(d.Id())
143-
if err != nil {
144-
return diag.Errorf("Failed to convert topic id %s to int: %s", d.Id(), err)
153+
_, _, err = client.Topics.UpdateTopic(topicID, options, gitlab.WithContext(ctx))
154+
if err != nil {
155+
return diag.Errorf("Failed to update topic %d: %s", topicID, err)
156+
}
157+
158+
return nil
145159
}
146-
_, _, err = client.Topics.UpdateTopic(topicID, options, gitlab.WithContext(ctx))
147-
if err != nil {
148-
return diag.Errorf("Failed to update topic %d: %s", topicID, err)
160+
161+
log.Printf("[DEBUG] delete gitlab topic %s", d.Id())
162+
163+
if _, err = client.Topics.DeleteTopic(topicID, gitlab.WithContext(ctx)); err != nil {
164+
return diag.Errorf("Failed to delete topic %d: %s", topicID, err)
149165
}
166+
150167
return nil
151168
}

internal/provider/resource_gitlab_topic_test.go

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ func TestAccGitlabTopic_basic(t *testing.T) {
3030
}),
3131
),
3232
},
33+
// Verify import
34+
{
35+
ResourceName: "gitlab_topic.foo",
36+
ImportState: true,
37+
ImportStateVerify: true,
38+
ImportStateVerifyIgnore: []string{
39+
"soft_destroy",
40+
},
41+
},
3342
// Update the topics values
3443
{
3544
Config: testAccGitlabTopicFullConfig(rInt),
@@ -41,6 +50,15 @@ func TestAccGitlabTopic_basic(t *testing.T) {
4150
}),
4251
),
4352
},
53+
// Verify import
54+
{
55+
ResourceName: "gitlab_topic.foo",
56+
ImportState: true,
57+
ImportStateVerify: true,
58+
ImportStateVerifyIgnore: []string{
59+
"soft_destroy",
60+
},
61+
},
4462
// Update the topics values back to their initial state
4563
{
4664
Config: testAccGitlabTopicRequiredConfig(rInt),
@@ -51,6 +69,15 @@ func TestAccGitlabTopic_basic(t *testing.T) {
5169
}),
5270
),
5371
},
72+
// Verify import
73+
{
74+
ResourceName: "gitlab_topic.foo",
75+
ImportState: true,
76+
ImportStateVerify: true,
77+
ImportStateVerifyIgnore: []string{
78+
"soft_destroy",
79+
},
80+
},
5481
// Updating the topic to have a description before it is deleted
5582
{
5683
Config: testAccGitlabTopicFullConfig(rInt),
@@ -75,6 +102,26 @@ func TestAccGitlabTopic_basic(t *testing.T) {
75102
})
76103
}
77104

105+
func TestAccGitlabTopic_softDestroy(t *testing.T) {
106+
var topic gitlab.Topic
107+
rInt := acctest.RandInt()
108+
109+
resource.Test(t, resource.TestCase{
110+
PreCheck: func() { testAccPreCheck(t) },
111+
ProviderFactories: providerFactories,
112+
CheckDestroy: testAccCheckGitlabTopicSoftDestroy,
113+
Steps: []resource.TestStep{
114+
// Create a topic with soft_destroy enabled
115+
{
116+
Config: testAccGitlabTopicSoftDestroyConfig(rInt),
117+
Check: resource.ComposeTestCheckFunc(
118+
testAccCheckGitlabTopicExists("gitlab_topic.foo", &topic),
119+
),
120+
},
121+
},
122+
})
123+
}
124+
78125
func testAccCheckGitlabTopicExists(n string, assign *gitlab.Topic) resource.TestCheckFunc {
79126
return func(s *terraform.State) (err error) {
80127

@@ -139,19 +186,48 @@ func testAccCheckGitlabTopicDestroy(s *terraform.State) (err error) {
139186
return err
140187
}
141188

142-
topic, resp, err := testGitlabClient.Topics.GetTopic(id)
189+
topic, _, err := testGitlabClient.Topics.GetTopic(id)
143190
if err == nil {
144191
if topic != nil && fmt.Sprintf("%d", topic.ID) == rs.Primary.ID {
192+
return fmt.Errorf("topic %s still exists", rs.Primary.ID)
193+
}
194+
}
195+
if !is404(err) {
196+
return err
197+
}
198+
return nil
199+
}
200+
return nil
201+
}
202+
203+
func testAccCheckGitlabTopicSoftDestroy(s *terraform.State) (err error) {
204+
205+
defer func() {
206+
if err != nil {
207+
err = fmt.Errorf("destroying gitlab topic failed: %w", err)
208+
}
209+
}()
210+
211+
for _, rs := range s.RootModule().Resources {
212+
if rs.Type != "gitlab_topic" {
213+
continue
214+
}
215+
216+
id, err := strconv.Atoi(rs.Primary.ID)
217+
if err != nil {
218+
return err
219+
}
145220

221+
topic, _, err := testGitlabClient.Topics.GetTopic(id)
222+
if err == nil {
223+
if topic != nil && fmt.Sprintf("%d", topic.ID) == rs.Primary.ID {
146224
if topic.Description != "" {
147225
return fmt.Errorf("topic still has a description")
148226
}
149-
150-
// TODO: Return error as soon as deleting a topic is supported
151227
return nil
152228
}
153229
}
154-
if resp.StatusCode != 404 {
230+
if !is404(err) {
155231
return err
156232
}
157233
return nil
@@ -163,7 +239,6 @@ func testAccGitlabTopicRequiredConfig(rInt int) string {
163239
return fmt.Sprintf(`
164240
resource "gitlab_topic" "foo" {
165241
name = "foo-req-%d"
166-
soft_destroy = true
167242
}`, rInt)
168243
}
169244

@@ -172,6 +247,15 @@ func testAccGitlabTopicFullConfig(rInt int) string {
172247
resource "gitlab_topic" "foo" {
173248
name = "foo-full-%d"
174249
description = "Terraform acceptance tests"
250+
}`, rInt)
251+
}
252+
253+
func testAccGitlabTopicSoftDestroyConfig(rInt int) string {
254+
return fmt.Sprintf(`
255+
resource "gitlab_topic" "foo" {
256+
name = "foo-soft-destroy-%d"
257+
description = "Terraform acceptance tests"
258+
175259
soft_destroy = true
176260
}`, rInt)
177261
}

0 commit comments

Comments
 (0)