terraform-provider-greenhost/digitalocean/datasource_digitalocean_spa...

223 lines
5.3 KiB
Go

package digitalocean
import (
"bytes"
"fmt"
"log"
"regexp"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)
func dataSourceDigitalOceanSpacesBucketObject() *schema.Resource {
return &schema.Resource{
Read: dataSourceDigitalOceanSpacesBucketObjectRead,
Schema: map[string]*schema.Schema{
"bucket": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringIsNotEmpty,
},
"region": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringIsNotEmpty,
},
// computed attributes
"body": {
Type: schema.TypeString,
Computed: true,
},
"cache_control": {
Type: schema.TypeString,
Computed: true,
},
"content_disposition": {
Type: schema.TypeString,
Computed: true,
},
"content_encoding": {
Type: schema.TypeString,
Computed: true,
},
"content_language": {
Type: schema.TypeString,
Computed: true,
},
"content_length": {
Type: schema.TypeInt,
Computed: true,
},
"content_type": {
Type: schema.TypeString,
Computed: true,
},
"etag": {
Type: schema.TypeString,
Computed: true,
},
"expiration": {
Type: schema.TypeString,
Computed: true,
},
"expires": {
Type: schema.TypeString,
Computed: true,
},
"key": {
Type: schema.TypeString,
Required: true,
},
"last_modified": {
Type: schema.TypeString,
Computed: true,
},
"metadata": {
Type: schema.TypeMap,
Computed: true,
},
"range": {
Type: schema.TypeString,
Optional: true,
},
"version_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"website_redirect_location": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
func dataSourceDigitalOceanSpacesBucketObjectRead(d *schema.ResourceData, meta interface{}) error {
region := d.Get("region").(string)
client, err := meta.(*CombinedConfig).spacesClient(region)
if err != nil {
return err
}
conn := s3.New(client)
bucket := d.Get("bucket").(string)
key := d.Get("key").(string)
input := s3.HeadObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
}
if v, ok := d.GetOk("range"); ok {
input.Range = aws.String(v.(string))
}
if v, ok := d.GetOk("version_id"); ok {
input.VersionId = aws.String(v.(string))
}
versionText := ""
uniqueId := bucket + "/" + key
if v, ok := d.GetOk("version_id"); ok {
versionText = fmt.Sprintf(" of version %q", v.(string))
uniqueId += "@" + v.(string)
}
log.Printf("[DEBUG] Reading S3 Bucket Object: %s", input)
out, err := conn.HeadObject(&input)
if err != nil {
return fmt.Errorf("Failed getting S3 object: %s Bucket: %q Object: %q", err, bucket, key)
}
if out.DeleteMarker != nil && *out.DeleteMarker {
return fmt.Errorf("Requested S3 object %q%s has been deleted",
bucket+key, versionText)
}
log.Printf("[DEBUG] Received S3 object: %s", out)
d.SetId(uniqueId)
d.Set("cache_control", out.CacheControl)
d.Set("content_disposition", out.ContentDisposition)
d.Set("content_encoding", out.ContentEncoding)
d.Set("content_language", out.ContentLanguage)
d.Set("content_length", out.ContentLength)
d.Set("content_type", out.ContentType)
// See https://forums.aws.amazon.com/thread.jspa?threadID=44003
d.Set("etag", strings.Trim(*out.ETag, `"`))
d.Set("expiration", out.Expiration)
d.Set("expires", out.Expires)
d.Set("last_modified", out.LastModified.Format(time.RFC1123))
d.Set("metadata", pointersMapToStringList(out.Metadata))
d.Set("version_id", out.VersionId)
d.Set("website_redirect_location", out.WebsiteRedirectLocation)
if isContentTypeAllowed(out.ContentType) {
input := s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
}
if v, ok := d.GetOk("range"); ok {
input.Range = aws.String(v.(string))
}
if out.VersionId != nil {
input.VersionId = out.VersionId
}
out, err := conn.GetObject(&input)
if err != nil {
return fmt.Errorf("Failed getting S3 object: %s", err)
}
buf := new(bytes.Buffer)
bytesRead, err := buf.ReadFrom(out.Body)
if err != nil {
return fmt.Errorf("Failed reading content of S3 object (%s): %s",
uniqueId, err)
}
log.Printf("[INFO] Saving %d bytes from S3 object %s", bytesRead, uniqueId)
d.Set("body", buf.String())
} else {
contentType := ""
if out.ContentType == nil {
contentType = "<EMPTY>"
} else {
contentType = *out.ContentType
}
log.Printf("[INFO] Ignoring body of S3 object %s with Content-Type %q",
uniqueId, contentType)
}
return nil
}
// This is to prevent potential issues w/ binary files
// and generally unprintable characters
// See https://github.com/hashicorp/terraform/pull/3858#issuecomment-156856738
func isContentTypeAllowed(contentType *string) bool {
if contentType == nil {
return false
}
allowedContentTypes := []*regexp.Regexp{
regexp.MustCompile("^text/.+"),
regexp.MustCompile("^application/json$"),
}
for _, r := range allowedContentTypes {
if r.MatchString(*contentType) {
return true
}
}
return false
}