Skip to content

Commit d8baf3c

Browse files
committed
Object Store object singular data source
1 parent f141376 commit d8baf3c

File tree

7 files changed

+369
-1
lines changed

7 files changed

+369
-1
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
## 3.12.1 (Unreleased)
1+
## 3.13.0 (Unreleased)
2+
3+
### Added
4+
- Added singular data source for Object Storage objects
5+
26
## 3.12.0 (January 15, 2019)
37

48
### Added

docs/examples/object_storage/object.tf

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,21 @@ data "oci_objectstorage_objects" "objects1" {
5454
bucket = "${oci_objectstorage_bucket.bucket1.name}"
5555
}
5656

57+
data "oci_objectstorage_object" "object" {
58+
namespace = "${data.oci_objectstorage_namespace.ns.namespace}"
59+
bucket = "${oci_objectstorage_bucket.bucket1.name}"
60+
object = "index.html"
61+
}
62+
63+
output object-data {
64+
value = <<EOF
65+
66+
content = ${data.oci_objectstorage_object.object.content}
67+
content-length = ${data.oci_objectstorage_object.object.content_length}
68+
content-type = ${data.oci_objectstorage_object.object.content_type}
69+
EOF
70+
}
71+
5772
output object-head-data {
5873
value = <<EOF
5974
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
// Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
2+
3+
package provider
4+
5+
import (
6+
"context"
7+
"fmt"
8+
"io/ioutil"
9+
"log"
10+
"strconv"
11+
12+
"github.com/hashicorp/terraform/helper/schema"
13+
oci_object_storage "github.com/oracle/oci-go-sdk/objectstorage"
14+
)
15+
16+
func ObjectDataSource() *schema.Resource {
17+
return &schema.Resource{
18+
Read: readSingularObject,
19+
Schema: map[string]*schema.Schema{
20+
"bucket": {
21+
Type: schema.TypeString,
22+
Required: true,
23+
},
24+
"namespace": {
25+
Type: schema.TypeString,
26+
Required: true,
27+
},
28+
"object": {
29+
Type: schema.TypeString,
30+
Required: true,
31+
},
32+
"content_length_limit": {
33+
Type: schema.TypeInt,
34+
Optional: true,
35+
//default value is 1MB
36+
Default: 1048576,
37+
},
38+
39+
// Computed
40+
"content": {
41+
Type: schema.TypeString,
42+
Computed: true,
43+
},
44+
"content_encoding": {
45+
Type: schema.TypeString,
46+
Computed: true,
47+
},
48+
"content_language": {
49+
Type: schema.TypeString,
50+
Computed: true,
51+
},
52+
"content_length": {
53+
Type: schema.TypeString,
54+
Computed: true,
55+
},
56+
"content_md5": {
57+
Type: schema.TypeString,
58+
Computed: true,
59+
},
60+
"content_type": {
61+
Type: schema.TypeString,
62+
Computed: true,
63+
},
64+
"metadata": {
65+
Type: schema.TypeMap,
66+
Elem: schema.TypeString,
67+
Computed: true,
68+
},
69+
},
70+
}
71+
}
72+
73+
func readSingularObject(d *schema.ResourceData, m interface{}) error {
74+
sync := &ObjectDataSourceCrud{}
75+
sync.D = d
76+
sync.Client = m.(*OracleClients).objectStorageClient
77+
78+
return ReadResource(sync)
79+
}
80+
81+
type ObjectDataSourceCrud struct {
82+
D *schema.ResourceData
83+
Client *oci_object_storage.ObjectStorageClient
84+
Res *oci_object_storage.GetObjectResponse
85+
}
86+
87+
func (s *ObjectDataSourceCrud) VoidState() {
88+
s.D.SetId("")
89+
}
90+
91+
func (s *ObjectDataSourceCrud) Get() error {
92+
93+
headObjectRequest := &oci_object_storage.HeadObjectRequest{}
94+
95+
if bucket, ok := s.D.GetOkExists("bucket"); ok {
96+
bucketName := bucket.(string)
97+
headObjectRequest.BucketName = &bucketName
98+
}
99+
100+
if namespace, ok := s.D.GetOkExists("namespace"); ok {
101+
namespaceName := namespace.(string)
102+
headObjectRequest.NamespaceName = &namespaceName
103+
}
104+
105+
if object, ok := s.D.GetOkExists("object"); ok {
106+
objectName := object.(string)
107+
headObjectRequest.ObjectName = &objectName
108+
}
109+
110+
headObjectRequest.RequestMetadata.RetryPolicy = getRetryPolicy(false, "object_storage")
111+
112+
headObjectResponse, err := s.Client.HeadObject(context.Background(), *headObjectRequest)
113+
if err != nil {
114+
return err
115+
}
116+
117+
if contentLengthLimit, ok := s.D.GetOkExists("content_length_limit"); ok {
118+
tmpInt64 := int64(contentLengthLimit.(int))
119+
120+
if tmpInt64 < *headObjectResponse.ContentLength {
121+
return fmt.Errorf("the requested object's content length is %v the limit is set to %v", *headObjectResponse.ContentLength, tmpInt64)
122+
}
123+
124+
}
125+
126+
request := oci_object_storage.GetObjectRequest{}
127+
request.NamespaceName = headObjectRequest.NamespaceName
128+
request.BucketName = headObjectRequest.BucketName
129+
request.ObjectName = headObjectRequest.ObjectName
130+
131+
response, err := s.Client.GetObject(context.Background(), request)
132+
if err != nil {
133+
return err
134+
}
135+
136+
s.Res = &response
137+
return nil
138+
}
139+
140+
func (s *ObjectDataSourceCrud) SetData() error {
141+
if s.Res == nil {
142+
return nil
143+
}
144+
145+
s.D.SetId(GenerateDataSourceID())
146+
147+
contentReader := s.Res.Content
148+
contentArray, err := ioutil.ReadAll(contentReader)
149+
if err != nil {
150+
log.Printf("unable to read 'content' from response. Error: %v", err)
151+
} else {
152+
s.D.Set("content", string(contentArray))
153+
}
154+
155+
if s.Res.ContentEncoding != nil {
156+
s.D.Set("content_encoding", *s.Res.ContentEncoding)
157+
}
158+
159+
if s.Res.ContentLanguage != nil {
160+
s.D.Set("content_language", *s.Res.ContentLanguage)
161+
}
162+
163+
if s.Res.ContentLength != nil {
164+
s.D.Set("content_length", strconv.FormatInt(*s.Res.ContentLength, 10))
165+
}
166+
167+
if s.Res.ContentMd5 != nil {
168+
s.D.Set("content_md5", *s.Res.ContentMd5)
169+
}
170+
171+
if s.Res.ContentType != nil {
172+
s.D.Set("content_type", *s.Res.ContentType)
173+
}
174+
175+
if s.Res.OpcMeta != nil {
176+
if err := s.D.Set("metadata", s.Res.OpcMeta); err != nil {
177+
log.Printf("unable to set 'metadata'. Error: %v", err)
178+
}
179+
}
180+
181+
return nil
182+
}

oci/object_storage_object_test.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ var (
3838
"values": Representation{repType: Required, create: []string{`${oci_objectstorage_object.test_object.object}`}},
3939
}
4040

41+
objectSingularDataSourceRepresentation = map[string]interface{}{
42+
"bucket": Representation{repType: Required, create: `${oci_objectstorage_bucket.test_bucket.name}`},
43+
"namespace": Representation{repType: Required, create: `${oci_objectstorage_bucket.test_bucket.namespace}`},
44+
"object": Representation{repType: Required, create: `my-test-object-3`},
45+
"content_length_limit": Representation{repType: Optional, create: `17`, update: `15`},
46+
}
47+
4148
objectRepresentation = map[string]interface{}{
4249
"bucket": Representation{repType: Required, create: `${oci_objectstorage_bucket.test_bucket.name}`},
4350
"content": Representation{repType: Optional, create: `content`, update: `<a1>content</a1>`},
@@ -61,6 +68,7 @@ func TestObjectStorageObjectResource_basic(t *testing.T) {
6168

6269
resourceName := "oci_objectstorage_object.test_object"
6370
datasourceName := "data.oci_objectstorage_objects.test_objects"
71+
singularDatasourceName := "data.oci_objectstorage_object.test_object"
6472

6573
var resId, resId2 string
6674
hexSum := md5.Sum([]byte("content"))
@@ -183,6 +191,47 @@ func TestObjectStorageObjectResource_basic(t *testing.T) {
183191
},
184192
),
185193
},
194+
// verify singular datasource
195+
{
196+
Config: config + compartmentIdVariableStr + ObjectResourceDependencies +
197+
generateResourceFromRepresentationMap("oci_objectstorage_object", "test_object", Optional, Update,
198+
getUpdatedRepresentationCopy("object", Representation{repType: Required, create: `my-test-object-1`, update: `my-test-object-3`}, objectRepresentation)) +
199+
generateDataSourceFromRepresentationMap("oci_objectstorage_object", "test_object", Required, Create, objectSingularDataSourceRepresentation),
200+
Check: resource.ComposeAggregateTestCheckFunc(
201+
resource.TestCheckResourceAttr(singularDatasourceName, "content_encoding", "identity"),
202+
resource.TestCheckResourceAttr(singularDatasourceName, "content_language", "en-CA"),
203+
resource.TestCheckResourceAttr(singularDatasourceName, "content_length", "16"),
204+
resource.TestCheckResourceAttrSet(singularDatasourceName, "content_md5"),
205+
resource.TestCheckResourceAttr(singularDatasourceName, "content_type", "text/xml"),
206+
resource.TestCheckResourceAttr(singularDatasourceName, "bucket", "my-test-1"),
207+
resource.TestCheckResourceAttrSet(singularDatasourceName, "content"),
208+
resource.TestCheckResourceAttr(singularDatasourceName, "content", "<a1>content</a1>"),
209+
resource.TestCheckResourceAttr(singularDatasourceName, "metadata.%", "1"),
210+
resource.TestCheckResourceAttr(singularDatasourceName, "metadata.content-type", "text/xml"),
211+
resource.TestCheckResourceAttrSet(singularDatasourceName, "namespace"),
212+
resource.TestCheckResourceAttr(singularDatasourceName, "object", "my-test-object-3"),
213+
),
214+
},
215+
{
216+
Config: config + compartmentIdVariableStr + ObjectResourceDependencies +
217+
generateResourceFromRepresentationMap("oci_objectstorage_object", "test_object", Optional, Update,
218+
getUpdatedRepresentationCopy("object", Representation{repType: Required, create: `my-test-object-1`, update: `my-test-object-3`}, objectRepresentation)) +
219+
generateDataSourceFromRepresentationMap("oci_objectstorage_object", "test_object", Optional, Create, objectSingularDataSourceRepresentation),
220+
Check: resource.ComposeAggregateTestCheckFunc(
221+
resource.TestCheckResourceAttr(singularDatasourceName, "content_encoding", "identity"),
222+
resource.TestCheckResourceAttr(singularDatasourceName, "content_language", "en-CA"),
223+
resource.TestCheckResourceAttr(singularDatasourceName, "content_length", "16"),
224+
resource.TestCheckResourceAttrSet(singularDatasourceName, "content_md5"),
225+
resource.TestCheckResourceAttr(singularDatasourceName, "content_type", "text/xml"),
226+
resource.TestCheckResourceAttr(singularDatasourceName, "bucket", "my-test-1"),
227+
resource.TestCheckResourceAttrSet(singularDatasourceName, "content"),
228+
resource.TestCheckResourceAttr(singularDatasourceName, "content", "<a1>content</a1>"),
229+
resource.TestCheckResourceAttr(singularDatasourceName, "metadata.%", "1"),
230+
resource.TestCheckResourceAttr(singularDatasourceName, "metadata.content-type", "text/xml"),
231+
resource.TestCheckResourceAttrSet(singularDatasourceName, "namespace"),
232+
resource.TestCheckResourceAttr(singularDatasourceName, "object", "my-test-object-3"),
233+
),
234+
},
186235
// verify datasource
187236
{
188237
Config: config +
@@ -233,6 +282,70 @@ func TestObjectStorageObjectResource_basic(t *testing.T) {
233282
})
234283
}
235284

285+
func TestObjectStorageObjectResource_failContentLengthLimit(t *testing.T) {
286+
provider := testAccProvider
287+
config := testProviderConfig()
288+
289+
compartmentId := getEnvSettingWithBlankDefault("compartment_ocid")
290+
compartmentIdVariableStr := fmt.Sprintf("variable \"compartment_id\" { default = \"%s\" }\n", compartmentId)
291+
292+
var resourceName = "oci_objectstorage_object.test_object"
293+
var failObjectName, failBucketName, failNamespaceName string
294+
295+
resource.Test(t, resource.TestCase{
296+
PreCheck: func() { testAccPreCheck(t) },
297+
Providers: map[string]terraform.ResourceProvider{
298+
"oci": provider,
299+
},
300+
Steps: []resource.TestStep{
301+
{
302+
Config: config + compartmentIdVariableStr + ObjectResourceDependencies +
303+
generateResourceFromRepresentationMap("oci_objectstorage_object", "test_object", Optional, Update,
304+
getUpdatedRepresentationCopy("object", Representation{repType: Required, create: `my-test-object-1`, update: `my-test-object-3`}, objectRepresentation)),
305+
Check: resource.ComposeAggregateTestCheckFunc(
306+
func(s *terraform.State) (err error) {
307+
failObjectName, err = fromInstanceState(s, resourceName, "object")
308+
if err != nil {
309+
return err
310+
}
311+
failBucketName, err = fromInstanceState(s, resourceName, "bucket")
312+
if err != nil {
313+
return err
314+
}
315+
failNamespaceName, err = fromInstanceState(s, resourceName, "namespace")
316+
return err
317+
}),
318+
},
319+
{
320+
Config: config + compartmentIdVariableStr + ObjectResourceDependencies +
321+
generateResourceFromRepresentationMap("oci_objectstorage_object", "test_object", Optional, Update,
322+
getUpdatedRepresentationCopy("object", Representation{repType: Required, create: `my-test-object-1`, update: `my-test-object-3`}, objectRepresentation)) +
323+
generateDataSourceFromRepresentationMap("oci_objectstorage_object", "test_object", Optional, Update, objectSingularDataSourceRepresentation),
324+
ExpectError: regexp.MustCompile("the requested object's content length is"),
325+
},
326+
},
327+
})
328+
329+
//destroy test will be skipped since there is no state after the error in Get
330+
if failObjectName != "" && failBucketName != "" && failNamespaceName != "" {
331+
client := testAccProvider.Meta().(*OracleClients).objectStorageClient
332+
_, objectErr := client.DeleteObject(context.Background(), oci_object_storage.DeleteObjectRequest{
333+
NamespaceName: &failNamespaceName,
334+
BucketName: &failBucketName,
335+
ObjectName: &failObjectName,
336+
})
337+
338+
_, bucketErr := client.DeleteBucket(context.Background(), oci_object_storage.DeleteBucketRequest{
339+
NamespaceName: &failNamespaceName,
340+
BucketName: &failBucketName,
341+
})
342+
343+
if objectErr != nil || bucketErr != nil {
344+
t.Errorf("failed to delete resources for the test: %v, %v", objectErr, bucketErr)
345+
}
346+
}
347+
}
348+
236349
// This test is separated from the above test due to weird behavior from Terraform test framework.
237350
// An test step that results in an error will result in the state being voided. Isolate such test steps to
238351
// avoid interfering with regular tests that Create/Update resources.

oci/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ func dataSourcesMap() map[string]*schema.Resource {
365365
"oci_objectstorage_namespace": NamespaceDataSource(),
366366
"oci_objectstorage_namespace_metadata": NamespaceMetadataDataSource(),
367367
"oci_objectstorage_object_head": ObjectHeadDataSource(),
368+
"oci_objectstorage_object": ObjectDataSource(),
368369
"oci_objectstorage_objects": ObjectsDataSource(),
369370
"oci_objectstorage_preauthrequest": PreauthenticatedRequestDataSource(),
370371
"oci_objectstorage_preauthrequests": PreauthenticatedRequestsDataSource(),

0 commit comments

Comments
 (0)