Skip to content

Commit 834c4b9

Browse files
Upstream fix for GCS object owner is optional (#5618) (#11006)
* GCS object owner is optional - There are some cases (one case is described in #10342 (comment)), where `Object.owner` is missing, which leads to nil pointer dereference. Fixes #10342 * remove comment * skip vcr test Co-authored-by: Aniruddha Maru <[email protected]> Signed-off-by: Modular Magician <[email protected]> Co-authored-by: Aniruddha Maru <[email protected]>
1 parent c2864ef commit 834c4b9

File tree

3 files changed

+99
-5
lines changed

3 files changed

+99
-5
lines changed

.changelog/5618.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
storage: Fixed bug where the provider crashes when `Object.owner` is missing when using `google_storage_object_acl`
3+
```

google/resource_storage_object_acl.go

+12-3
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ func resourceStorageObjectAclDiff(_ context.Context, diff *schema.ResourceDiff,
8080
return nil
8181
}
8282

83-
objectOwner := sObject.Owner.Entity
83+
var objectOwner string
84+
if sObject.Owner != nil {
85+
objectOwner = sObject.Owner.Entity
86+
}
8487
ownerRole := fmt.Sprintf("%s:%s", "OWNER", objectOwner)
8588
oldRoleEntity, newRoleEntity := diff.GetChange("role_entity")
8689

@@ -146,7 +149,10 @@ func resourceStorageObjectAclCreate(d *schema.ResourceData, meta interface{}) er
146149
return fmt.Errorf("Error reading object %s in %s: %v", object, bucket, err)
147150
}
148151

149-
objectOwner := sObject.Owner.Entity
152+
var objectOwner string
153+
if sObject.Owner != nil {
154+
objectOwner = sObject.Owner.Entity
155+
}
150156
roleEntitiesUpstream, err := getRoleEntitiesAsStringsFromApi(config, bucket, object, userAgent)
151157
if err != nil {
152158
return fmt.Errorf("Error reading object %s in %s: %v", object, bucket, err)
@@ -227,7 +233,10 @@ func resourceStorageObjectAclUpdate(d *schema.ResourceData, meta interface{}) er
227233
return fmt.Errorf("Error reading object %s in %s: %v", object, bucket, err)
228234
}
229235

230-
objectOwner := sObject.Owner.Entity
236+
var objectOwner string
237+
if sObject.Owner != nil {
238+
objectOwner = sObject.Owner.Entity
239+
}
231240

232241
o, n := d.GetChange("role_entity")
233242
create, update, remove, err := getRoleEntityChange(

google/resource_storage_object_acl_test.go

+84-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
package google
22

33
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
47
"fmt"
8+
"io"
59
"io/ioutil"
10+
"net/http"
611
"testing"
712

13+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
814
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
15+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
916
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
1017
)
1118

@@ -31,8 +38,7 @@ func TestAccStorageObjectAcl_basic(t *testing.T) {
3138
}
3239
testAccPreCheck(t)
3340
},
34-
Providers: testAccProviders,
35-
CheckDestroy: testAccStorageObjectAclDestroyProducer(t),
41+
Providers: testAccProviders,
3642
Steps: []resource.TestStep{
3743
{
3844
Config: testGoogleStorageObjectsAclBasic1(bucketName, objectName),
@@ -278,6 +284,82 @@ func TestAccStorageObjectAcl_unordered(t *testing.T) {
278284
})
279285
}
280286

287+
// a round tripper that returns fake response for get object API removing `owner` attribute
288+
// it only modifies the response once, since otherwise resource will fail to delete.
289+
type testRoundTripper struct {
290+
http.RoundTripper
291+
bucketName, objectName string
292+
done bool
293+
}
294+
295+
func (t *testRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
296+
response, err := t.RoundTripper.RoundTrip(r)
297+
if err != nil {
298+
return response, err
299+
}
300+
expectedPath := fmt.Sprintf("/storage/v1/b/%s/o/%s", t.bucketName, t.objectName)
301+
if t.done || r.URL.Path != expectedPath || r.Host != "storage.googleapis.com" {
302+
return response, err
303+
}
304+
t.done = true
305+
responseBytes, err := ioutil.ReadAll(response.Body)
306+
if err != nil {
307+
return response, err
308+
}
309+
var responseMap map[string]json.RawMessage
310+
err = json.Unmarshal(responseBytes, &responseMap)
311+
if err != nil {
312+
return response, err
313+
}
314+
delete(responseMap, "owner")
315+
responseBytes, err = json.Marshal(responseMap)
316+
if err != nil {
317+
return response, err
318+
}
319+
response.Body = io.NopCloser(bytes.NewBuffer(responseBytes))
320+
return response, nil
321+
}
322+
323+
// Test that we don't fail if there's no owner for object
324+
func TestAccStorageObjectAcl_noOwner(t *testing.T) {
325+
skipIfVcr(t)
326+
t.Parallel()
327+
328+
bucketName := testBucketName(t)
329+
objectName := testAclObjectName(t)
330+
objectData := []byte("data data data")
331+
if err := ioutil.WriteFile(tfObjectAcl.Name(), objectData, 0644); err != nil {
332+
t.Errorf("error writing file: %v", err)
333+
}
334+
provider := Provider()
335+
oldConfigureFunc := provider.ConfigureContextFunc
336+
provider.ConfigureContextFunc = func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
337+
c, diagnostics := oldConfigureFunc(ctx, d)
338+
config := c.(*Config)
339+
roundTripper := &testRoundTripper{RoundTripper: config.client.Transport, bucketName: bucketName, objectName: objectName}
340+
config.client.Transport = roundTripper
341+
return c, diagnostics
342+
}
343+
providers := map[string]*schema.Provider{
344+
"google": provider,
345+
}
346+
vcrTest(t, resource.TestCase{
347+
PreCheck: func() {
348+
if errObjectAcl != nil {
349+
panic(errObjectAcl)
350+
}
351+
testAccPreCheck(t)
352+
},
353+
Providers: providers,
354+
Steps: []resource.TestStep{
355+
{
356+
Config: testGoogleStorageObjectsAclBasic1(bucketName, objectName),
357+
ExpectNonEmptyPlan: true,
358+
},
359+
},
360+
})
361+
}
362+
281363
func testAccCheckGoogleStorageObjectAcl(t *testing.T, bucket, object, roleEntityS string) resource.TestCheckFunc {
282364
return func(s *terraform.State) error {
283365
roleEntity, _ := getRoleEntityPair(roleEntityS)

0 commit comments

Comments
 (0)