github.com/recobe182/terraform@v0.8.5-0.20170117231232-49ab22a935b7/builtin/providers/aws/data_source_aws_s3_bucket_object.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"regexp"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/aws/aws-sdk-go/aws"
    12  	"github.com/aws/aws-sdk-go/service/s3"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  )
    15  
    16  func dataSourceAwsS3BucketObject() *schema.Resource {
    17  	return &schema.Resource{
    18  		Read: dataSourceAwsS3BucketObjectRead,
    19  
    20  		Schema: map[string]*schema.Schema{
    21  			"body": &schema.Schema{
    22  				Type:     schema.TypeString,
    23  				Computed: true,
    24  			},
    25  			"bucket": &schema.Schema{
    26  				Type:     schema.TypeString,
    27  				Required: true,
    28  			},
    29  			"cache_control": &schema.Schema{
    30  				Type:     schema.TypeString,
    31  				Computed: true,
    32  			},
    33  			"content_disposition": &schema.Schema{
    34  				Type:     schema.TypeString,
    35  				Computed: true,
    36  			},
    37  			"content_encoding": &schema.Schema{
    38  				Type:     schema.TypeString,
    39  				Computed: true,
    40  			},
    41  			"content_language": &schema.Schema{
    42  				Type:     schema.TypeString,
    43  				Computed: true,
    44  			},
    45  			"content_length": &schema.Schema{
    46  				Type:     schema.TypeInt,
    47  				Computed: true,
    48  			},
    49  			"content_type": &schema.Schema{
    50  				Type:     schema.TypeString,
    51  				Computed: true,
    52  			},
    53  			"etag": &schema.Schema{
    54  				Type:     schema.TypeString,
    55  				Computed: true,
    56  			},
    57  			"expiration": &schema.Schema{
    58  				Type:     schema.TypeString,
    59  				Computed: true,
    60  			},
    61  			"expires": &schema.Schema{
    62  				Type:     schema.TypeString,
    63  				Computed: true,
    64  			},
    65  			"key": &schema.Schema{
    66  				Type:     schema.TypeString,
    67  				Required: true,
    68  			},
    69  			"last_modified": &schema.Schema{
    70  				Type:     schema.TypeString,
    71  				Computed: true,
    72  			},
    73  			"metadata": &schema.Schema{
    74  				Type:     schema.TypeMap,
    75  				Computed: true,
    76  			},
    77  			"range": &schema.Schema{
    78  				Type:     schema.TypeString,
    79  				Optional: true,
    80  			},
    81  			"server_side_encryption": &schema.Schema{
    82  				Type:     schema.TypeString,
    83  				Computed: true,
    84  			},
    85  			"sse_kms_key_id": &schema.Schema{
    86  				Type:     schema.TypeString,
    87  				Computed: true,
    88  			},
    89  			"storage_class": &schema.Schema{
    90  				Type:     schema.TypeString,
    91  				Computed: true,
    92  			},
    93  			"version_id": &schema.Schema{
    94  				Type:     schema.TypeString,
    95  				Optional: true,
    96  				Computed: true,
    97  			},
    98  			"website_redirect_location": &schema.Schema{
    99  				Type:     schema.TypeString,
   100  				Computed: true,
   101  			},
   102  		},
   103  	}
   104  }
   105  
   106  func dataSourceAwsS3BucketObjectRead(d *schema.ResourceData, meta interface{}) error {
   107  	conn := meta.(*AWSClient).s3conn
   108  
   109  	bucket := d.Get("bucket").(string)
   110  	key := d.Get("key").(string)
   111  
   112  	input := s3.HeadObjectInput{
   113  		Bucket: aws.String(bucket),
   114  		Key:    aws.String(key),
   115  	}
   116  	if v, ok := d.GetOk("range"); ok {
   117  		input.Range = aws.String(v.(string))
   118  	}
   119  	if v, ok := d.GetOk("version_id"); ok {
   120  		input.VersionId = aws.String(v.(string))
   121  	}
   122  
   123  	versionText := ""
   124  	uniqueId := bucket + "/" + key
   125  	if v, ok := d.GetOk("version_id"); ok {
   126  		versionText = fmt.Sprintf(" of version %q", v.(string))
   127  		uniqueId += "@" + v.(string)
   128  	}
   129  
   130  	log.Printf("[DEBUG] Reading S3 object: %s", input)
   131  	out, err := conn.HeadObject(&input)
   132  	if err != nil {
   133  		return fmt.Errorf("Failed getting S3 object: %s", err)
   134  	}
   135  	if out.DeleteMarker != nil && *out.DeleteMarker == true {
   136  		return fmt.Errorf("Requested S3 object %q%s has been deleted",
   137  			bucket+key, versionText)
   138  	}
   139  
   140  	log.Printf("[DEBUG] Received S3 object: %s", out)
   141  
   142  	d.SetId(uniqueId)
   143  
   144  	d.Set("cache_control", out.CacheControl)
   145  	d.Set("content_disposition", out.ContentDisposition)
   146  	d.Set("content_encoding", out.ContentEncoding)
   147  	d.Set("content_language", out.ContentLanguage)
   148  	d.Set("content_length", out.ContentLength)
   149  	d.Set("content_type", out.ContentType)
   150  	// See https://forums.aws.amazon.com/thread.jspa?threadID=44003
   151  	d.Set("etag", strings.Trim(*out.ETag, `"`))
   152  	d.Set("expiration", out.Expiration)
   153  	d.Set("expires", out.Expires)
   154  	d.Set("last_modified", out.LastModified.Format(time.RFC1123))
   155  	d.Set("metadata", pointersMapToStringList(out.Metadata))
   156  	d.Set("server_side_encryption", out.ServerSideEncryption)
   157  	d.Set("sse_kms_key_id", out.SSEKMSKeyId)
   158  	d.Set("version_id", out.VersionId)
   159  	d.Set("website_redirect_location", out.WebsiteRedirectLocation)
   160  
   161  	// The "STANDARD" (which is also the default) storage
   162  	// class when set would not be included in the results.
   163  	d.Set("storage_class", s3.StorageClassStandard)
   164  	if out.StorageClass != nil {
   165  		d.Set("storage_class", out.StorageClass)
   166  	}
   167  
   168  	if isContentTypeAllowed(out.ContentType) {
   169  		input := s3.GetObjectInput{
   170  			Bucket: aws.String(bucket),
   171  			Key:    aws.String(key),
   172  		}
   173  		if v, ok := d.GetOk("range"); ok {
   174  			input.Range = aws.String(v.(string))
   175  		}
   176  		if out.VersionId != nil {
   177  			input.VersionId = out.VersionId
   178  		}
   179  		out, err := conn.GetObject(&input)
   180  		if err != nil {
   181  			return fmt.Errorf("Failed getting S3 object: %s", err)
   182  		}
   183  
   184  		buf := new(bytes.Buffer)
   185  		bytesRead, err := buf.ReadFrom(out.Body)
   186  		if err != nil {
   187  			return fmt.Errorf("Failed reading content of S3 object (%s): %s",
   188  				uniqueId, err)
   189  		}
   190  		log.Printf("[INFO] Saving %d bytes from S3 object %s", bytesRead, uniqueId)
   191  		d.Set("body", buf.String())
   192  	} else {
   193  		contentType := ""
   194  		if out.ContentType == nil {
   195  			contentType = "<EMPTY>"
   196  		} else {
   197  			contentType = *out.ContentType
   198  		}
   199  
   200  		log.Printf("[INFO] Ignoring body of S3 object %s with Content-Type %q",
   201  			uniqueId, contentType)
   202  	}
   203  
   204  	return nil
   205  }
   206  
   207  // This is to prevent potential issues w/ binary files
   208  // and generally unprintable characters
   209  // See https://github.com/hashicorp/terraform/pull/3858#issuecomment-156856738
   210  func isContentTypeAllowed(contentType *string) bool {
   211  	if contentType == nil {
   212  		return false
   213  	}
   214  
   215  	allowedContentTypes := []*regexp.Regexp{
   216  		regexp.MustCompile("^text/.+"),
   217  		regexp.MustCompile("^application/json$"),
   218  	}
   219  
   220  	for _, r := range allowedContentTypes {
   221  		if r.MatchString(*contentType) {
   222  			return true
   223  		}
   224  	}
   225  
   226  	return false
   227  }