github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/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  			"tags": tagsSchemaComputed(),
   104  		},
   105  	}
   106  }
   107  
   108  func dataSourceAwsS3BucketObjectRead(d *schema.ResourceData, meta interface{}) error {
   109  	conn := meta.(*AWSClient).s3conn
   110  
   111  	bucket := d.Get("bucket").(string)
   112  	key := d.Get("key").(string)
   113  
   114  	input := s3.HeadObjectInput{
   115  		Bucket: aws.String(bucket),
   116  		Key:    aws.String(key),
   117  	}
   118  	if v, ok := d.GetOk("range"); ok {
   119  		input.Range = aws.String(v.(string))
   120  	}
   121  	if v, ok := d.GetOk("version_id"); ok {
   122  		input.VersionId = aws.String(v.(string))
   123  	}
   124  
   125  	versionText := ""
   126  	uniqueId := bucket + "/" + key
   127  	if v, ok := d.GetOk("version_id"); ok {
   128  		versionText = fmt.Sprintf(" of version %q", v.(string))
   129  		uniqueId += "@" + v.(string)
   130  	}
   131  
   132  	log.Printf("[DEBUG] Reading S3 object: %s", input)
   133  	out, err := conn.HeadObject(&input)
   134  	if err != nil {
   135  		return fmt.Errorf("Failed getting S3 object: %s Bucket: %q Object: %q", err, bucket, key)
   136  	}
   137  	if out.DeleteMarker != nil && *out.DeleteMarker == true {
   138  		return fmt.Errorf("Requested S3 object %q%s has been deleted",
   139  			bucket+key, versionText)
   140  	}
   141  
   142  	log.Printf("[DEBUG] Received S3 object: %s", out)
   143  
   144  	d.SetId(uniqueId)
   145  
   146  	d.Set("cache_control", out.CacheControl)
   147  	d.Set("content_disposition", out.ContentDisposition)
   148  	d.Set("content_encoding", out.ContentEncoding)
   149  	d.Set("content_language", out.ContentLanguage)
   150  	d.Set("content_length", out.ContentLength)
   151  	d.Set("content_type", out.ContentType)
   152  	// See https://forums.aws.amazon.com/thread.jspa?threadID=44003
   153  	d.Set("etag", strings.Trim(*out.ETag, `"`))
   154  	d.Set("expiration", out.Expiration)
   155  	d.Set("expires", out.Expires)
   156  	d.Set("last_modified", out.LastModified.Format(time.RFC1123))
   157  	d.Set("metadata", pointersMapToStringList(out.Metadata))
   158  	d.Set("server_side_encryption", out.ServerSideEncryption)
   159  	d.Set("sse_kms_key_id", out.SSEKMSKeyId)
   160  	d.Set("version_id", out.VersionId)
   161  	d.Set("website_redirect_location", out.WebsiteRedirectLocation)
   162  
   163  	// The "STANDARD" (which is also the default) storage
   164  	// class when set would not be included in the results.
   165  	d.Set("storage_class", s3.StorageClassStandard)
   166  	if out.StorageClass != nil {
   167  		d.Set("storage_class", out.StorageClass)
   168  	}
   169  
   170  	if isContentTypeAllowed(out.ContentType) {
   171  		input := s3.GetObjectInput{
   172  			Bucket: aws.String(bucket),
   173  			Key:    aws.String(key),
   174  		}
   175  		if v, ok := d.GetOk("range"); ok {
   176  			input.Range = aws.String(v.(string))
   177  		}
   178  		if out.VersionId != nil {
   179  			input.VersionId = out.VersionId
   180  		}
   181  		out, err := conn.GetObject(&input)
   182  		if err != nil {
   183  			return fmt.Errorf("Failed getting S3 object: %s", err)
   184  		}
   185  
   186  		buf := new(bytes.Buffer)
   187  		bytesRead, err := buf.ReadFrom(out.Body)
   188  		if err != nil {
   189  			return fmt.Errorf("Failed reading content of S3 object (%s): %s",
   190  				uniqueId, err)
   191  		}
   192  		log.Printf("[INFO] Saving %d bytes from S3 object %s", bytesRead, uniqueId)
   193  		d.Set("body", buf.String())
   194  	} else {
   195  		contentType := ""
   196  		if out.ContentType == nil {
   197  			contentType = "<EMPTY>"
   198  		} else {
   199  			contentType = *out.ContentType
   200  		}
   201  
   202  		log.Printf("[INFO] Ignoring body of S3 object %s with Content-Type %q",
   203  			uniqueId, contentType)
   204  	}
   205  
   206  	tagResp, err := conn.GetObjectTagging(
   207  		&s3.GetObjectTaggingInput{
   208  			Bucket: aws.String(bucket),
   209  			Key:    aws.String(key),
   210  		})
   211  	if err != nil {
   212  		return err
   213  	}
   214  	d.Set("tags", tagsToMapS3(tagResp.TagSet))
   215  
   216  	return nil
   217  }
   218  
   219  // This is to prevent potential issues w/ binary files
   220  // and generally unprintable characters
   221  // See https://github.com/hashicorp/terraform/pull/3858#issuecomment-156856738
   222  func isContentTypeAllowed(contentType *string) bool {
   223  	if contentType == nil {
   224  		return false
   225  	}
   226  
   227  	allowedContentTypes := []*regexp.Regexp{
   228  		regexp.MustCompile("^text/.+"),
   229  		regexp.MustCompile("^application/json$"),
   230  	}
   231  
   232  	for _, r := range allowedContentTypes {
   233  		if r.MatchString(*contentType) {
   234  			return true
   235  		}
   236  	}
   237  
   238  	return false
   239  }