Skip to content

Commit 349a7bd

Browse files
committed
Support for deletion of tags and tag namespaces
1 parent c389448 commit 349a7bd

15 files changed

+618
-7
lines changed

oci/crud_helpers.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/hashicorp/terraform/helper/resource"
1717
"github.com/hashicorp/terraform/helper/schema"
1818
oci_common "github.com/oracle/oci-go-sdk/common"
19+
oci_identity "github.com/oracle/oci-go-sdk/identity"
1920
oci_load_balancer "github.com/oracle/oci-go-sdk/loadbalancer"
2021

2122
"github.com/terraform-providers/terraform-provider-oci/httpreplay"
@@ -215,6 +216,44 @@ func LoadBalancerWaitForWorkRequest(client *oci_load_balancer.LoadBalancerClient
215216
return nil
216217
}
217218

219+
func IdentityWaitForWorkRequest(client *oci_identity.IdentityClient, d *schema.ResourceData, wr *oci_identity.WorkRequest, retryPolicy *oci_common.RetryPolicy, timeout time.Duration) error {
220+
stateConf := &resource.StateChangeConf{
221+
Pending: []string{
222+
string(oci_identity.WorkRequestStatusInProgress),
223+
string(oci_identity.WorkRequestStatusAccepted),
224+
string(oci_identity.WorkRequestStatusCanceling),
225+
},
226+
Target: []string{
227+
string(oci_identity.WorkRequestStatusSucceeded),
228+
string(oci_identity.WorkRequestStatusFailed),
229+
string(oci_identity.WorkRequestStatusCanceled),
230+
},
231+
Refresh: func() (interface{}, string, error) {
232+
getWorkRequestRequest := oci_identity.GetWorkRequestRequest{}
233+
getWorkRequestRequest.WorkRequestId = wr.Id
234+
getWorkRequestRequest.RequestMetadata.RetryPolicy = retryPolicy
235+
workRequestResponse, err := client.GetWorkRequest(context.Background(), getWorkRequestRequest)
236+
wr = &workRequestResponse.WorkRequest
237+
return wr, string(wr.Status), err
238+
},
239+
Timeout: timeout,
240+
}
241+
242+
// Should not wait when in replay mode
243+
if httpreplay.ShouldRetryImmediately() {
244+
stateConf.PollInterval = 1
245+
}
246+
247+
if _, e := stateConf.WaitForState(); e != nil {
248+
return e
249+
}
250+
251+
if wr.Status == oci_identity.WorkRequestStatusFailed || wr.Status == oci_identity.WorkRequestStatusCanceled {
252+
return fmt.Errorf("WorkRequest FAILED: %+v", wr.Errors)
253+
}
254+
return nil
255+
}
256+
218257
func CreateDBSystemResource(d *schema.ResourceData, sync ResourceCreator) error {
219258
if e := sync.Create(); e != nil {
220259
return e

oci/identity_cost_tracking_tag_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func TestIdentityCostTrackingTagResource_basic(t *testing.T) {
5353
resource.TestCheckResourceAttrSet(datasourceName, "tags.0.is_cost_tracking"),
5454
resource.TestCheckResourceAttrSet(datasourceName, "tags.0.is_retired"),
5555
resource.TestCheckResourceAttrSet(datasourceName, "tags.0.name"),
56+
resource.TestCheckResourceAttrSet(datasourceName, "tags.0.state"),
5657
resource.TestCheckResourceAttrSet(datasourceName, "tags.0.tag_namespace_id"),
5758
resource.TestCheckResourceAttrSet(datasourceName, "tags.0.tag_namespace_name"),
5859
resource.TestCheckResourceAttrSet(datasourceName, "tags.0.time_created"),

oci/identity_cost_tracking_tags_data_source.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ func IdentityCostTrackingTagsDataSource() *schema.Resource {
6262
Type: schema.TypeString,
6363
Computed: true,
6464
},
65+
"state": {
66+
Type: schema.TypeString,
67+
Computed: true,
68+
},
6569
"tag_namespace_id": {
6670
Type: schema.TypeString,
6771
Computed: true,
@@ -180,6 +184,8 @@ func (s *IdentityCostTrackingTagsDataSourceCrud) SetData() error {
180184
costTrackingTag["name"] = *r.Name
181185
}
182186

187+
costTrackingTag["state"] = r.LifecycleState
188+
183189
if r.TagNamespaceId != nil {
184190
costTrackingTag["tag_namespace_id"] = *r.TagNamespaceId
185191
}

oci/identity_tag_namespace_resource.go

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ func IdentityTagNamespaceResource() *schema.Resource {
6262
},
6363

6464
// Computed
65+
"state": {
66+
Type: schema.TypeString,
67+
Computed: true,
68+
},
6569
"time_created": {
6670
Type: schema.TypeString,
6771
Computed: true,
@@ -95,7 +99,19 @@ func updateIdentityTagNamespace(d *schema.ResourceData, m interface{}) error {
9599
}
96100

97101
func deleteIdentityTagNamespace(d *schema.ResourceData, m interface{}) error {
98-
return nil
102+
// Only empty tag namespaces can be deleted, to execute our tests we don't want to delete namespaces as we create
103+
// namespaces with tags and deleting a tag is a sequential and time consuming operation allowed one per tenancy
104+
importIfExists, _ := strconv.ParseBool(getEnvSettingWithDefault("tags_import_if_exists", "false"))
105+
if importIfExists {
106+
return nil
107+
}
108+
109+
sync := &IdentityTagNamespaceResourceCrud{}
110+
sync.D = d
111+
sync.Client = m.(*OracleClients).identityClient
112+
sync.DisableNotFoundRetries = true
113+
114+
return DeleteResource(d, sync)
99115
}
100116

101117
type IdentityTagNamespaceResourceCrud struct {
@@ -109,6 +125,28 @@ func (s *IdentityTagNamespaceResourceCrud) ID() string {
109125
return *s.Res.Id
110126
}
111127

128+
func (s *IdentityTagNamespaceResourceCrud) CreatedPending() []string {
129+
return []string{}
130+
}
131+
132+
func (s *IdentityTagNamespaceResourceCrud) CreatedTarget() []string {
133+
return []string{
134+
string(oci_identity.TagNamespaceLifecycleStateActive),
135+
}
136+
}
137+
138+
func (s *IdentityTagNamespaceResourceCrud) DeletedPending() []string {
139+
return []string{
140+
string(oci_identity.TagNamespaceLifecycleStateDeleting),
141+
}
142+
}
143+
144+
func (s *IdentityTagNamespaceResourceCrud) DeletedTarget() []string {
145+
return []string{
146+
string(oci_identity.TagNamespaceLifecycleStateDeleted),
147+
}
148+
}
149+
112150
func (s *IdentityTagNamespaceResourceCrud) Create() error {
113151
request := oci_identity.CreateTagNamespaceRequest{}
114152

@@ -250,6 +288,18 @@ func (s *IdentityTagNamespaceResourceCrud) Update() error {
250288
return nil
251289
}
252290

291+
func (s *IdentityTagNamespaceResourceCrud) Delete() error {
292+
request := oci_identity.DeleteTagNamespaceRequest{}
293+
294+
tmp := s.D.Id()
295+
request.TagNamespaceId = &tmp
296+
297+
request.RequestMetadata.RetryPolicy = getRetryPolicy(s.DisableNotFoundRetries, "identity")
298+
299+
_, err := s.Client.DeleteTagNamespace(context.Background(), request)
300+
return err
301+
}
302+
253303
func (s *IdentityTagNamespaceResourceCrud) SetData() error {
254304
if s.Res.CompartmentId != nil {
255305
s.D.Set("compartment_id", *s.Res.CompartmentId)
@@ -273,6 +323,8 @@ func (s *IdentityTagNamespaceResourceCrud) SetData() error {
273323
s.D.Set("name", *s.Res.Name)
274324
}
275325

326+
s.D.Set("state", s.Res.LifecycleState)
327+
276328
if s.Res.TimeCreated != nil {
277329
s.D.Set("time_created", s.Res.TimeCreated.String())
278330
}

oci/identity_tag_namespace_test.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@
33
package provider
44

55
import (
6+
"context"
67
"fmt"
8+
"strconv"
79
"testing"
10+
"time"
11+
12+
"github.com/oracle/oci-go-sdk/common"
813

914
"github.com/hashicorp/terraform/helper/resource"
15+
"github.com/hashicorp/terraform/helper/schema"
1016
"github.com/hashicorp/terraform/terraform"
17+
oci_identity "github.com/oracle/oci-go-sdk/identity"
1118

1219
"github.com/terraform-providers/terraform-provider-oci/httpreplay"
1320
)
@@ -19,6 +26,7 @@ var (
1926
tagNamespaceDataSourceRepresentation = map[string]interface{}{
2027
"compartment_id": Representation{repType: Required, create: `${var.compartment_id}`},
2128
"include_subcompartments": Representation{repType: Optional, create: `false`},
29+
"state": Representation{repType: Optional, create: `AVAILABLE`},
2230
"filter": RepresentationGroup{Required, tagNamespaceDataSourceFilterRepresentation}}
2331
tagNamespaceDataSourceFilterRepresentation = map[string]interface{}{
2432
"name": Representation{repType: Required, create: `id`},
@@ -59,6 +67,7 @@ func TestIdentityTagNamespaceResource_basic(t *testing.T) {
5967
Providers: map[string]terraform.ResourceProvider{
6068
"oci": provider,
6169
},
70+
//CheckDestroy: testAccCheckIdentityTagNamespaceDestroy,
6271
Steps: []resource.TestStep{
6372
// verify create
6473
{
@@ -160,6 +169,7 @@ func TestIdentityTagNamespaceResource_basic(t *testing.T) {
160169
Check: resource.ComposeAggregateTestCheckFunc(
161170
resource.TestCheckResourceAttr(datasourceName, "compartment_id", compartmentId),
162171
resource.TestCheckResourceAttr(datasourceName, "include_subcompartments", "false"),
172+
resource.TestCheckResourceAttr(datasourceName, "state", "AVAILABLE"),
163173

164174
resource.TestCheckResourceAttr(datasourceName, "tag_namespaces.#", "1"),
165175
resource.TestCheckResourceAttr(datasourceName, "tag_namespaces.0.compartment_id", compartmentId),
@@ -169,6 +179,7 @@ func TestIdentityTagNamespaceResource_basic(t *testing.T) {
169179
resource.TestCheckResourceAttrSet(datasourceName, "tag_namespaces.0.id"),
170180
resource.TestCheckResourceAttrSet(datasourceName, "tag_namespaces.0.is_retired"),
171181
resource.TestCheckResourceAttr(datasourceName, "tag_namespaces.0.name", "BillingTags"),
182+
resource.TestCheckResourceAttrSet(datasourceName, "tag_namespaces.0.state"),
172183
resource.TestCheckResourceAttrSet(datasourceName, "tag_namespaces.0.time_created"),
173184
),
174185
},
@@ -183,3 +194,137 @@ func TestIdentityTagNamespaceResource_basic(t *testing.T) {
183194
},
184195
})
185196
}
197+
198+
func testAccCheckIdentityTagNamespaceDestroy(s *terraform.State) error {
199+
noResourceFound := true
200+
client := testAccProvider.Meta().(*OracleClients).identityClient
201+
for _, rs := range s.RootModule().Resources {
202+
if rs.Type == "oci_identity_tag_namespace" {
203+
noResourceFound = false
204+
request := oci_identity.GetTagNamespaceRequest{}
205+
206+
tmp := rs.Primary.ID
207+
request.TagNamespaceId = &tmp
208+
209+
request.RequestMetadata.RetryPolicy = getRetryPolicy(true, "identity")
210+
211+
response, err := client.GetTagNamespace(context.Background(), request)
212+
213+
if err == nil {
214+
deletedLifecycleStates := map[string]bool{
215+
string(oci_identity.TagNamespaceLifecycleStateDeleted): true,
216+
}
217+
if _, ok := deletedLifecycleStates[string(response.LifecycleState)]; !ok {
218+
//resource lifecycle state is not in expected deleted lifecycle states.
219+
return fmt.Errorf("resource lifecycle state: %s is not in expected deleted lifecycle states", response.LifecycleState)
220+
}
221+
//resource lifecycle state is in expected deleted lifecycle states. continue with next one.
222+
continue
223+
}
224+
225+
//Verify that exception is for '404 not found'.
226+
if failure, isServiceError := common.IsServiceError(err); !isServiceError || failure.GetHTTPStatusCode() != 404 {
227+
return err
228+
}
229+
}
230+
}
231+
if noResourceFound {
232+
return fmt.Errorf("at least one resource was expected from the state file, but could not be found")
233+
}
234+
235+
return nil
236+
}
237+
238+
func init() {
239+
if DependencyGraph == nil {
240+
initDependencyGraph()
241+
}
242+
if !inSweeperExcludeList("IdentityTagNamespace") {
243+
resource.AddTestSweepers("IdentityTagNamespace", &resource.Sweeper{
244+
Name: "IdentityTagNamespace",
245+
Dependencies: DependencyGraph["tagNamespace"],
246+
F: sweepIdentityTagNamespaceResource,
247+
})
248+
}
249+
}
250+
251+
func sweepIdentityTagNamespaceResource(compartment string) error {
252+
// prevent tag deletion when testing, as its a time consuming and sequential operation permitted one per tenancy.
253+
importIfExists, _ := strconv.ParseBool(getEnvSettingWithDefault("tags_import_if_exists", "false"))
254+
if importIfExists {
255+
return nil
256+
}
257+
258+
identityClient := GetTestClients(&schema.ResourceData{}).identityClient
259+
tagNamespaceIds, err := getTagNamespaceIds(compartment)
260+
if err != nil {
261+
return err
262+
}
263+
264+
// clean all tags in namespaces
265+
err = sweepIdentityTagResource(compartment)
266+
if err != nil {
267+
return err
268+
}
269+
270+
for _, tagNamespaceId := range tagNamespaceIds {
271+
if ok := SweeperDefaultResourceId[tagNamespaceId]; !ok {
272+
deleteTagNamespaceRequest := oci_identity.DeleteTagNamespaceRequest{}
273+
274+
deleteTagNamespaceRequest.TagNamespaceId = &tagNamespaceId
275+
276+
deleteTagNamespaceRequest.RequestMetadata.RetryPolicy = getRetryPolicy(true, "identity")
277+
_, error := identityClient.DeleteTagNamespace(context.Background(), deleteTagNamespaceRequest)
278+
if error != nil {
279+
fmt.Printf("Error deleting TagNamespace %s %s, It is possible that the resource is already deleted. Please verify manually \n", tagNamespaceId, error)
280+
continue
281+
}
282+
waitTillCondition(testAccProvider, &tagNamespaceId, tagNamespaceSweepWaitCondition, time.Duration(3*time.Minute),
283+
tagNamespaceSweepResponseFetchOperation, "identity", true)
284+
}
285+
}
286+
return nil
287+
}
288+
289+
func getTagNamespaceIds(compartment string) ([]string, error) {
290+
ids := getResourceIdsToSweep(compartment, "TagNamespaceId")
291+
if ids != nil {
292+
return ids, nil
293+
}
294+
var resourceIds []string
295+
compartmentId := compartment
296+
identityClient := GetTestClients(&schema.ResourceData{}).identityClient
297+
298+
listTagNamespacesRequest := oci_identity.ListTagNamespacesRequest{}
299+
listTagNamespacesRequest.CompartmentId = &compartmentId
300+
listTagNamespacesRequest.LifecycleState = oci_identity.TagNamespaceLifecycleStateActive
301+
listTagNamespacesResponse, err := identityClient.ListTagNamespaces(context.Background(), listTagNamespacesRequest)
302+
303+
if err != nil {
304+
return resourceIds, fmt.Errorf("Error getting TagNamespace list for compartment id : %s , %s \n", compartmentId, err)
305+
}
306+
for _, tagNamespace := range listTagNamespacesResponse.Items {
307+
id := *tagNamespace.Id
308+
resourceIds = append(resourceIds, id)
309+
addResourceIdToSweeperResourceIdMap(compartmentId, "TagNamespaceId", id)
310+
}
311+
return resourceIds, nil
312+
}
313+
314+
func tagNamespaceSweepWaitCondition(response common.OCIOperationResponse) bool {
315+
// Only stop if the resource is available beyond 3 mins. As there could be an issue for the sweeper to delete the resource and manual intervention required.
316+
if tagNamespaceResponse, ok := response.Response.(oci_identity.GetTagNamespaceResponse); ok {
317+
return tagNamespaceResponse.LifecycleState != oci_identity.TagNamespaceLifecycleStateDeleted
318+
}
319+
return false
320+
}
321+
322+
func tagNamespaceSweepResponseFetchOperation(client *OracleClients, resourceId *string, retryPolicy *common.RetryPolicy) error {
323+
_, err := client.identityClient.GetTagNamespace(context.Background(), oci_identity.GetTagNamespaceRequest{
324+
TagNamespaceId: resourceId,
325+
RequestMetadata: common.RequestMetadata{
326+
RetryPolicy: retryPolicy,
327+
},
328+
})
329+
return err
330+
}

oci/identity_tag_namespaces_data_source.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ func IdentityTagNamespacesDataSource() *schema.Resource {
2222
Type: schema.TypeBool,
2323
Optional: true,
2424
},
25+
"state": {
26+
Type: schema.TypeString,
27+
Optional: true,
28+
},
2529
"tag_namespaces": {
2630
Type: schema.TypeList,
2731
Computed: true,
@@ -62,6 +66,10 @@ func (s *IdentityTagNamespacesDataSourceCrud) Get() error {
6266
request.IncludeSubcompartments = &tmp
6367
}
6468

69+
if state, ok := s.D.GetOkExists("state"); ok {
70+
request.LifecycleState = oci_identity.TagNamespaceLifecycleStateEnum(state.(string))
71+
}
72+
6573
request.RequestMetadata.RetryPolicy = getRetryPolicy(false, "identity")
6674

6775
response, err := s.Client.ListTagNamespaces(context.Background(), request)
@@ -120,6 +128,8 @@ func (s *IdentityTagNamespacesDataSourceCrud) SetData() error {
120128
tagNamespace["name"] = *r.Name
121129
}
122130

131+
tagNamespace["state"] = r.LifecycleState
132+
123133
if r.TimeCreated != nil {
124134
tagNamespace["time_created"] = r.TimeCreated.String()
125135
}

0 commit comments

Comments
 (0)