Skip to content

Commit 4fd21a6

Browse files
author
Ravi Tandon
committed
Fixed Object Storage Object Resource to be a stateful resource
1 parent c47ffda commit 4fd21a6

File tree

4 files changed

+93
-63
lines changed

4 files changed

+93
-63
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
### Changed
44
- LoadBalancer BackendSets to have TypeSet for Backends to avoid out of order diffs
55

6+
### Fixed
7+
- Regression in handling of failed work-requests to pass the errors to the user and fail the apply
8+
69
## 3.11.0 (December 18, 2018)
710

811
### Added

oci/crud_helpers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ func CreateResource(d *schema.ResourceData, sync ResourceCreator) error {
252252

253253
if stateful, ok := sync.(StatefullyCreatedResource); ok {
254254
if e := waitForStateRefresh(stateful, d.Timeout(schema.TimeoutCreate), "creation", stateful.CreatedPending(), stateful.CreatedTarget()); e != nil {
255-
if stateful.State() == string(oci_load_balancer.WorkRequestLifecycleStateFailed) {
255+
if stateful.State() == FAILED {
256256
// Remove resource from state if asynchronous work request has failed so that it is recreated on next apply
257257
// TODO: automatic retry on WorkRequestFailed
258258
sync.VoidState()

oci/object_storage_object_resource.go

Lines changed: 83 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,13 @@ func ObjectResource() *schema.Resource {
175175
},
176176

177177
// Computed
178-
"copy_state": {
178+
// @CODEGEN 12/20/2018 - Even though Object resource is not stateful for content and multi-part variations
179+
// making those variations stateful to match the logic for copy case to ensure that provider does not fail during state polling due to missing state property
180+
"state": {
179181
Type: schema.TypeString,
180182
Computed: true,
181183
},
182-
"copy_work_request_id": {
184+
"work_request_id": {
183185
Type: schema.TypeString,
184186
Computed: true,
185187
},
@@ -257,14 +259,18 @@ func (s *ObjectResourceCrud) createMultiPartObject() error {
257259
}
258260

259261
multipartUploadData.ObjectStorageClient = s.Client
260-
261262
multipartUploadData.RequestMetadata.RetryPolicy = getRetryPolicy(s.DisableNotFoundRetries, "object_storage")
263+
264+
s.D.Set("work_request_id", "")
265+
s.D.Set("state", oci_object_storage.WorkRequestStatusInProgress)
266+
262267
id, multipartInitErr := MultiPartUpload(multipartUploadData)
263268
if multipartInitErr != nil {
264269
return multipartInitErr
265270
}
266271

267272
s.D.SetId(id)
273+
s.D.Set("state", oci_object_storage.WorkRequestStatusCompleted)
268274

269275
return s.Get()
270276
}
@@ -347,37 +353,45 @@ func (s *ObjectResourceCrud) createCopyObject() error {
347353
copyObjectRequest.RequestMetadata.RetryPolicy = getRetryPolicy(s.DisableNotFoundRetries, "object_storage")
348354

349355
var workRequestId = ""
350-
if copyState, ok := s.D.GetOkExists("copy_state"); ok {
351-
if copyState == oci_object_storage.WorkRequestStatusInProgress {
352-
workRequestIdStateValue := s.D.Get("copy_work_request_id")
356+
if state, ok := s.D.GetOkExists("state"); ok {
357+
if state == oci_object_storage.WorkRequestStatusInProgress {
358+
workRequestIdStateValue := s.D.Get("work_request_id")
353359
workRequestId = workRequestIdStateValue.(string)
354360
}
355361
}
356362

357363
if workRequestId == "" {
358364
copyObjectResponse, err := s.SourceRegionClient.CopyObject(context.Background(), copyObjectRequest)
359365
if err != nil {
360-
s.D.Set("copy_state", string(oci_object_storage.WorkRequestStatusCanceled))
366+
s.D.Set("state", string(oci_object_storage.WorkRequestStatusCanceled))
361367
return err
362368
}
363369
workRequestId = *copyObjectResponse.OpcWorkRequestId
364370
}
365371

366-
s.D.Set("copy_work_request_id", workRequestId)
367-
s.D.Set("copy_state", string(oci_object_storage.WorkRequestStatusInProgress))
372+
s.D.Set("work_request_id", workRequestId)
373+
s.D.Set("state", string(oci_object_storage.WorkRequestStatusInProgress))
374+
375+
getWorkRequestRequest := oci_object_storage.GetWorkRequestRequest{}
376+
getWorkRequestRequest.WorkRequestId = &workRequestId
377+
getWorkRequestRequest.RequestMetadata.RetryPolicy = getRetryPolicy(s.DisableNotFoundRetries, "object_storage")
378+
workRequestResponse, err := s.Client.GetWorkRequest(context.Background(), getWorkRequestRequest)
379+
if err != nil {
380+
return err
381+
}
382+
s.WorkRequest = &workRequestResponse.WorkRequest
368383

369384
copyTimeout := *DefaultTimeout.Create
370-
err := copyObjectWaitForWorkRequest(&workRequestId, "object", copyTimeout, s.DisableNotFoundRetries, s.SourceRegionClient)
385+
err = copyObjectWaitForWorkRequest(&workRequestId, "object", copyTimeout, s.DisableNotFoundRetries, s.SourceRegionClient)
371386

372387
if err != nil {
373388
// we are not able to verify the state of workRequest
374-
s.D.Set("copy_work_request_id", "")
375-
s.D.Set("copy_state", string(oci_object_storage.WorkRequestStatusFailed))
389+
s.D.Set("state", string(oci_object_storage.WorkRequestStatusFailed))
376390
return err
377391
}
378392

379-
s.D.Set("copy_work_request_id", "")
380-
s.D.Set("copy_state", string(oci_object_storage.WorkRequestStatusCompleted))
393+
s.D.Set("work_request_id", "")
394+
s.D.Set("state", string(oci_object_storage.WorkRequestStatusCompleted))
381395
id := getId(*copyObjectRequest.DestinationNamespace, *copyObjectRequest.DestinationBucket, *copyObjectRequest.DestinationObjectName)
382396
s.D.SetId(id)
383397
return s.Get()
@@ -408,13 +422,14 @@ func deleteObject(d *schema.ResourceData, m interface{}) error {
408422
return DeleteResource(d, sync)
409423
}
410424

411-
// There's no struct to represent this in SDK, so we define our own.
425+
// There's no struct to represent this in SDK, so we define our own including a fake LifecycleState
412426
type ObjectStorageObject struct {
413-
namespaceName string
414-
bucketName string
415-
objectName string
416-
headObjectResponse oci_object_storage.HeadObjectResponse
417-
objectResponse oci_object_storage.GetObjectResponse
427+
NamespaceName string
428+
BucketName string
429+
ObjectName string
430+
HeadObjectResponse oci_object_storage.HeadObjectResponse
431+
ObjectResponse oci_object_storage.GetObjectResponse
432+
LifecycleState string
418433
}
419434

420435
type ObjectResourceCrud struct {
@@ -423,6 +438,7 @@ type ObjectResourceCrud struct {
423438
SourceRegionClient *oci_object_storage.ObjectStorageClient
424439
Res *ObjectStorageObject
425440
DisableNotFoundRetries bool
441+
WorkRequest *oci_object_storage.WorkRequest
426442
}
427443

428444
// @CODEGEN 2/2018: The existing provider returns a custom Id in following format:
@@ -447,7 +463,7 @@ func parseId(id string) (namespaceName string, bucketName string, objectName str
447463
}
448464

449465
func (s *ObjectResourceCrud) ID() string {
450-
return getId(s.Res.namespaceName, s.Res.bucketName, s.Res.objectName)
466+
return getId(s.Res.NamespaceName, s.Res.BucketName, s.Res.ObjectName)
451467
}
452468

453469
func (s *ObjectResourceCrud) Create() error {
@@ -534,6 +550,9 @@ func (s *ObjectResourceCrud) createContentObject() error {
534550

535551
request.RequestMetadata.RetryPolicy = getRetryPolicy(s.DisableNotFoundRetries, "object_storage")
536552

553+
s.D.Set("work_request_id", "")
554+
s.D.Set("state", oci_object_storage.WorkRequestStatusInProgress)
555+
537556
_, err := s.Client.PutObject(context.Background(), request)
538557
if err != nil {
539558
return err
@@ -543,7 +562,9 @@ func (s *ObjectResourceCrud) createContentObject() error {
543562
s.D.SetId(id)
544563

545564
// @CODEGEN 2/2018: PutObject() call doesn't return an object. Instead, use existing
546-
// Get() implementation to retrieve the state of the object.
565+
// Get() implementation to retrieve the state of the object and set its state to completed
566+
s.D.Set("state", oci_object_storage.WorkRequestStatusCompleted)
567+
547568
return s.Get()
548569
}
549570

@@ -572,27 +593,28 @@ func (s *ObjectResourceCrud) getObjectHead() error {
572593
}
573594

574595
s.Res = &ObjectStorageObject{
575-
namespaceName: *headObjectRequest.NamespaceName,
576-
bucketName: *headObjectRequest.BucketName,
577-
objectName: *headObjectRequest.ObjectName,
578-
headObjectResponse: headObjectResponse,
596+
NamespaceName: *headObjectRequest.NamespaceName,
597+
BucketName: *headObjectRequest.BucketName,
598+
ObjectName: *headObjectRequest.ObjectName,
599+
HeadObjectResponse: headObjectResponse,
600+
LifecycleState: s.D.Get("state").(string),
579601
}
580602

581603
return nil
582604
}
583605

584-
func (s *ObjectResourceCrud) updateCopyState() (bool, error) {
585-
if copyState, ok := s.D.GetOkExists("copy_state"); ok {
586-
if copyState == oci_object_storage.WorkRequestStatusInProgress {
606+
func (s *ObjectResourceCrud) updateState() (bool, error) {
607+
if state, ok := s.D.GetOkExists("state"); ok {
608+
if state == oci_object_storage.WorkRequestStatusInProgress {
587609

588-
if copyWRID, ok := s.D.GetOkExists("copy_work_request_id"); ok {
610+
if wrid, ok := s.D.GetOkExists("work_request_id"); ok {
589611
retryPolicy := getRetryPolicy(s.DisableNotFoundRetries, "object_storage")
590612
copyTimeout := DefaultTimeout.Create
591613
retryPolicy.ShouldRetryOperation = objectStorageWorkRequestShouldRetryFunc(*copyTimeout)
592614

593615
getWorkRequestRequest := oci_object_storage.GetWorkRequestRequest{}
594-
copyWRIDStr := copyWRID.(string)
595-
getWorkRequestRequest.WorkRequestId = &copyWRIDStr
616+
wridStr := wrid.(string)
617+
getWorkRequestRequest.WorkRequestId = &wridStr
596618
getWorkRequestRequest.RequestMetadata.RetryPolicy = retryPolicy
597619

598620
if sourceURI, ok := s.D.GetOkExists("source_uri_details"); ok && sourceURI != nil {
@@ -615,18 +637,18 @@ func (s *ObjectResourceCrud) updateCopyState() (bool, error) {
615637
}
616638

617639
wr := &workRequestResponse.WorkRequest
618-
s.D.Set("copy_state", string(wr.Status))
640+
s.D.Set("state", string(wr.Status))
619641

620642
if wr.Status == oci_object_storage.WorkRequestStatusInProgress {
621643
return false, nil
622644
}
623645

624-
s.D.Set("copy_work_request_id", "")
646+
s.D.Set("work_request_id", "")
625647
return true, nil
626648

627649
}
628650

629-
return false, fmt.Errorf("the state is incorrect. no copy_work_request_id found for the InProgress State")
651+
return false, fmt.Errorf("the state is incorrect. no work_request_id found for the InProgress State")
630652
}
631653

632654
return true, nil
@@ -656,7 +678,7 @@ func (s *ObjectResourceCrud) isCopyCreate() bool {
656678

657679
func (s *ObjectResourceCrud) Get() error {
658680

659-
workRequestFinished, err := s.updateCopyState()
681+
workRequestFinished, err := s.updateState()
660682
if err != nil {
661683
return err
662684
}
@@ -701,10 +723,11 @@ func (s *ObjectResourceCrud) getObject() error {
701723
// @CODEGEN 2/2018: We must store the response along with the identifiers that aren't
702724
// returned in the GetResponse.
703725
s.Res = &ObjectStorageObject{
704-
objectResponse: response,
705-
namespaceName: *request.NamespaceName,
706-
bucketName: *request.BucketName,
707-
objectName: *request.ObjectName,
726+
ObjectResponse: response,
727+
NamespaceName: *request.NamespaceName,
728+
BucketName: *request.BucketName,
729+
ObjectName: *request.ObjectName,
730+
LifecycleState: s.D.Get("state").(string),
708731
}
709732

710733
return nil
@@ -768,11 +791,11 @@ func (s *ObjectResourceCrud) SetData() error {
768791
}
769792

770793
func (s *ObjectResourceCrud) setDataObjectHead() error {
771-
s.D.Set("namespace", s.Res.namespaceName)
772-
s.D.Set("bucket", s.Res.bucketName)
773-
s.D.Set("object", s.Res.objectName)
794+
s.D.Set("namespace", s.Res.NamespaceName)
795+
s.D.Set("bucket", s.Res.BucketName)
796+
s.D.Set("object", s.Res.ObjectName)
774797

775-
response := s.Res.headObjectResponse
798+
response := s.Res.HeadObjectResponse
776799

777800
if response.ContentEncoding != nil {
778801
s.D.Set("content_encoding", *response.ContentEncoding)
@@ -808,42 +831,42 @@ func (s *ObjectResourceCrud) setDataObjectHead() error {
808831
}
809832

810833
func (s *ObjectResourceCrud) setDataObject() error {
811-
s.D.Set("namespace", s.Res.namespaceName)
812-
s.D.Set("bucket", s.Res.bucketName)
813-
s.D.Set("object", s.Res.objectName)
834+
s.D.Set("namespace", s.Res.NamespaceName)
835+
s.D.Set("bucket", s.Res.BucketName)
836+
s.D.Set("object", s.Res.ObjectName)
814837

815-
contentReader := s.Res.objectResponse.Content
838+
contentReader := s.Res.ObjectResponse.Content
816839
contentArray, err := ioutil.ReadAll(contentReader)
817840
if err != nil {
818841
log.Printf("Unable to read 'content' from response. Error: %q", err)
819842
return err
820843
}
821844
s.D.Set("content", contentArray)
822845

823-
if s.Res.objectResponse.ContentEncoding != nil {
824-
s.D.Set("content_encoding", *s.Res.objectResponse.ContentEncoding)
846+
if s.Res.ObjectResponse.ContentEncoding != nil {
847+
s.D.Set("content_encoding", *s.Res.ObjectResponse.ContentEncoding)
825848
}
826849

827-
if s.Res.objectResponse.ContentLanguage != nil {
828-
s.D.Set("content_language", *s.Res.objectResponse.ContentLanguage)
850+
if s.Res.ObjectResponse.ContentLanguage != nil {
851+
s.D.Set("content_language", *s.Res.ObjectResponse.ContentLanguage)
829852
}
830853

831-
if s.Res.objectResponse.ContentLength != nil {
832-
s.D.Set("content_length", strconv.FormatInt(*s.Res.objectResponse.ContentLength, 10))
854+
if s.Res.ObjectResponse.ContentLength != nil {
855+
s.D.Set("content_length", strconv.FormatInt(*s.Res.ObjectResponse.ContentLength, 10))
833856
}
834857

835-
if s.Res.objectResponse.ContentMd5 != nil {
836-
s.D.Set("content_md5", *s.Res.objectResponse.ContentMd5)
858+
if s.Res.ObjectResponse.ContentMd5 != nil {
859+
s.D.Set("content_md5", *s.Res.ObjectResponse.ContentMd5)
837860
}
838861

839-
if s.Res.objectResponse.ContentType != nil {
840-
s.D.Set("content_type", *s.Res.objectResponse.ContentType)
862+
if s.Res.ObjectResponse.ContentType != nil {
863+
s.D.Set("content_type", *s.Res.ObjectResponse.ContentType)
841864
}
842865

843-
if s.Res.objectResponse.OpcMeta != nil {
866+
if s.Res.ObjectResponse.OpcMeta != nil {
844867
// Note: regardless of what we sent to the SDK, the keys we get back from OpcMeta will always be
845868
// converted to lower case
846-
if err := s.D.Set("metadata", s.Res.objectResponse.OpcMeta); err != nil {
869+
if err := s.D.Set("metadata", s.Res.ObjectResponse.OpcMeta); err != nil {
847870
log.Printf("Unable to set 'metadata'. Error: %q", err)
848871
}
849872
}

oci/object_storage_object_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ func TestObjectStorageObjectResource_basic(t *testing.T) {
224224
// ResourceData to a state, Terraform strips it (possibly because ResourceData.Set stores it as a byte
225225
// array, while the schema expects a string?) Ignore this check as part of import tests for now.
226226
"content",
227+
"state",
228+
"work_request_id",
227229
},
228230
ResourceName: resourceName,
229231
},
@@ -528,6 +530,8 @@ func TestObjectStorageObjectResource_multipartUpload(t *testing.T) {
528530
ImportStateVerify: true,
529531
ImportStateVerifyIgnore: []string{
530532
"source",
533+
"state",
534+
"work_request_id",
531535
},
532536
ResourceName: resourceName,
533537
},
@@ -767,8 +771,8 @@ func TestObjectStorageObjectResource_crossRegionCopy(t *testing.T) {
767771
ImportStateVerify: true,
768772
ImportStateVerifyIgnore: []string{
769773
"source_uri_details",
770-
"copy_state",
771-
"copy_work_request_id",
774+
"state",
775+
"work_request_id",
772776
},
773777
ResourceName: resourceNameCopy,
774778
},

0 commit comments

Comments
 (0)