55 "crypto/md5"
66 "encoding/base64"
77 "io"
8+ "time"
89
910 "github.com/tidepool-org/platform/blob"
1011 blobStoreStructured "github.com/tidepool-org/platform/blob/store/structured"
@@ -19,6 +20,11 @@ import (
1920 structureValidator "github.com/tidepool-org/platform/structure/validator"
2021)
2122
23+ const (
24+ // arbritrary timeout value for cleanup operations such as deleting from S3 or Repo
25+ defaultCleanupTimeout = time .Second * 5
26+ )
27+
2228type Provider interface {
2329 BlobStructuredStore () blobStoreStructured.Store
2430 BlobUnstructuredStore () blobStoreUnstructured.Store
@@ -70,31 +76,40 @@ func (c *Client) Create(ctx context.Context, userID string, content *blob.Conten
7076 options .MediaType = content .MediaType
7177 err = c .BlobUnstructuredStore ().Put (ctx , userID , * result .ID , io .TeeReader (io .TeeReader (io .LimitReader (content .Body , blob .SizeMaximum + 1 ), hasher ), sizer ), options )
7278 if err != nil {
73- if _ , destroyErr := repository .Destroy (ctx , * result .ID , nil ); destroyErr != nil {
74- logger .WithError (destroyErr ).Error ("Unable to destroy blob after failure to put blob content" )
75- }
79+ alwaysDo (ctx , defaultCleanupTimeout , func (ctx context.Context ) error {
80+ if _ , destroyErr := repository .Destroy (ctx , * result .ID , nil ); destroyErr != nil {
81+ logger .WithError (destroyErr ).Error ("Unable to destroy blob after failure to put blob content" )
82+ }
83+ return nil
84+ })
7685 return nil , err
7786 }
7887
7988 size := sizer .Size
8089 if size > blob .SizeMaximum {
81- if _ , deleteErr := c .BlobUnstructuredStore ().Delete (ctx , userID , * result .ID ); deleteErr != nil {
82- logger .WithError (deleteErr ).Error ("Unable to delete blob content exceeding maximum size" )
83- }
84- if _ , destroyErr := repository .Destroy (ctx , * result .ID , nil ); destroyErr != nil {
85- logger .WithError (destroyErr ).Error ("Unable to destroy blob exceeding maximum size" )
86- }
90+ alwaysDo (ctx , defaultCleanupTimeout , func (ctx context.Context ) error {
91+ if _ , deleteErr := c .BlobUnstructuredStore ().Delete (ctx , userID , * result .ID ); deleteErr != nil {
92+ logger .WithError (deleteErr ).Error ("Unable to delete blob content exceeding maximum size" )
93+ }
94+ if _ , destroyErr := repository .Destroy (ctx , * result .ID , nil ); destroyErr != nil {
95+ logger .WithError (destroyErr ).Error ("Unable to destroy blob exceeding maximum size" )
96+ }
97+ return nil
98+ })
8799 return nil , request .ErrorResourceTooLarge ()
88100 }
89101
90102 digestMD5 := base64 .StdEncoding .EncodeToString (hasher .Sum (nil ))
91103 if content .DigestMD5 != nil && * content .DigestMD5 != digestMD5 {
92- if _ , deleteErr := c .BlobUnstructuredStore ().Delete (ctx , userID , * result .ID ); deleteErr != nil {
93- logger .WithError (deleteErr ).Error ("Unable to delete blob content with incorrect MD5 digest" )
94- }
95- if _ , destroyErr := repository .Destroy (ctx , * result .ID , nil ); destroyErr != nil {
96- logger .WithError (destroyErr ).Error ("Unable to destroy blob with incorrect MD5 digest" )
97- }
104+ alwaysDo (ctx , defaultCleanupTimeout , func (ctx context.Context ) error {
105+ if _ , deleteErr := c .BlobUnstructuredStore ().Delete (ctx , userID , * result .ID ); deleteErr != nil {
106+ logger .WithError (deleteErr ).Error ("Unable to delete blob content with incorrect MD5 digest" )
107+ }
108+ if _ , destroyErr := repository .Destroy (ctx , * result .ID , nil ); destroyErr != nil {
109+ logger .WithError (destroyErr ).Error ("Unable to destroy blob with incorrect MD5 digest" )
110+ }
111+ return nil
112+ })
98113 return nil , errors .WithSource (request .ErrorDigestsNotEqual (* content .DigestMD5 , digestMD5 ), structure .NewPointerSource ().WithReference ("digestMD5" ))
99114 }
100115
@@ -129,31 +144,41 @@ func (c *Client) CreateDeviceLogs(ctx context.Context, userID string, content *b
129144 options .MediaType = content .MediaType
130145 err = c .DeviceLogsUnstructuredStore ().Put (ctx , userID , * result .ID , io .TeeReader (io .TeeReader (io .LimitReader (content .Body , blob .SizeMaximum + 1 ), hasher ), sizer ), options )
131146 if err != nil {
132- if _ , destroyErr := repository .Destroy (ctx , * result .ID , nil ); destroyErr != nil {
133- logger .WithError (destroyErr ).Error ("Unable to destroy blob after failure to put blob content" )
134- }
147+ alwaysDo (ctx , defaultCleanupTimeout , func (ctx context.Context ) error {
148+ if _ , destroyErr := repository .Destroy (ctx , * result .ID , nil ); destroyErr != nil {
149+ logger .WithError (destroyErr ).Error ("Unable to destroy blob after failure to put blob content" )
150+ }
151+ return nil
152+ })
135153 return nil , err
136154 }
137155
138156 size := sizer .Size
139157 if size > blob .SizeMaximum {
140- if _ , deleteErr := c .DeviceLogsUnstructuredStore ().Delete (ctx , userID , * result .ID ); deleteErr != nil {
141- logger .WithError (deleteErr ).Error ("Unable to delete blob content exceeding maximum size" )
142- }
143- if _ , destroyErr := repository .Destroy (ctx , * result .ID , nil ); destroyErr != nil {
144- logger .WithError (destroyErr ).Error ("Unable to destroy blob exceeding maximum size" )
145- }
158+ alwaysDo (ctx , defaultCleanupTimeout , func (ctx context.Context ) error {
159+ if _ , deleteErr := c .DeviceLogsUnstructuredStore ().Delete (ctx , userID , * result .ID ); deleteErr != nil {
160+ logger .WithError (deleteErr ).Error ("Unable to delete blob content exceeding maximum size" )
161+ }
162+ if _ , destroyErr := repository .Destroy (ctx , * result .ID , nil ); destroyErr != nil {
163+ logger .WithError (destroyErr ).Error ("Unable to destroy blob exceeding maximum size" )
164+ }
165+ return nil
166+ })
146167 return nil , request .ErrorResourceTooLarge ()
147168 }
148169
149170 digestMD5 := base64 .StdEncoding .EncodeToString (hasher .Sum (nil ))
150171 if content .DigestMD5 != nil && * content .DigestMD5 != digestMD5 {
151- if _ , deleteErr := c .DeviceLogsUnstructuredStore ().Delete (ctx , userID , * result .ID ); deleteErr != nil {
152- logger .WithError (deleteErr ).Error ("Unable to delete blob content with incorrect MD5 digest" )
153- }
154- if _ , destroyErr := repository .Destroy (ctx , * result .ID , nil ); destroyErr != nil {
155- logger .WithError (destroyErr ).Error ("Unable to destroy blob with incorrect MD5 digest" )
156- }
172+ alwaysDo (ctx , defaultCleanupTimeout , func (ctx context.Context ) error {
173+ if _ , deleteErr := c .DeviceLogsUnstructuredStore ().Delete (ctx , userID , * result .ID ); deleteErr != nil {
174+ logger .WithError (deleteErr ).Error ("Unable to delete blob content with incorrect MD5 digest" )
175+ }
176+ if _ , destroyErr := repository .Destroy (ctx , * result .ID , nil ); destroyErr != nil {
177+ logger .WithError (destroyErr ).Error ("Unable to destroy blob with incorrect MD5 digest" )
178+ }
179+ return nil
180+ })
181+
157182 return nil , errors .WithSource (request .ErrorDigestsNotEqual (* content .DigestMD5 , digestMD5 ), structure .NewPointerSource ().WithReference ("digestMD5" ))
158183 }
159184
@@ -287,3 +312,13 @@ func (s *SizeWriter) Write(bites []byte) (int, error) {
287312 s .Size += length
288313 return length , nil
289314}
315+
316+ // alwaysDo performs an action given a context even if the context is
317+ // canceled or timed out. This is used if we have any cleanup functions that we
318+ // still want to perform and passing the parent context would time out any
319+ // child contexts.
320+ func alwaysDo (ctx context.Context , timeout time.Duration , fn func (ctx context.Context ) error ) error {
321+ newContext , cancel := context .WithTimeout (context .WithoutCancel (ctx ), timeout )
322+ defer cancel ()
323+ return fn (newContext )
324+ }
0 commit comments