This repository has been archived by the owner on Jan 17, 2021. It is now read-only.
/
object.go
129 lines (105 loc) · 2.91 KB
/
object.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package gos3headersetter
import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/s3"
)
// Object describes an S3 object. Use NewObject() to create new instances.
type Object struct {
Bucket string
Key string
newHeaders map[string]string
}
// NewObject returns a instance of Object.
func NewObject(bucket string, key string) Object {
return Object{
Bucket: bucket,
Key: key,
newHeaders: make(map[string]string),
}
}
func (o Object) String() string {
return fmt.Sprintf("s3://%s/%s", o.Bucket, o.Key)
}
// Apply applies the rules to this S3 object.
func (o Object) Apply(rules []Rule) error {
for _, rule := range rules {
o.queueRuleEffect(rule)
}
client, err := makeClient()
if err != nil {
return err
}
in := o.makeHeadObjectInput()
head, err := client.HeadObject(in)
if err != nil {
return err
}
copy := o.makeCopyObjectInput(head.Metadata)
hasChanges := o.updateCopyObjectInput(head, copy)
if !hasChanges {
o.log("no changes to apply")
return nil
}
o.log("Applying changes...")
_, err = client.CopyObject(copy)
return err
}
func (o Object) makeHeadObjectInput() *s3.HeadObjectInput {
return &s3.HeadObjectInput{
Bucket: &o.Bucket,
Key: &o.Key,
}
}
func (o Object) makeCopyObjectInput(metadata map[string]*string) *s3.CopyObjectInput {
return &s3.CopyObjectInput{
Bucket: &o.Bucket,
Key: &o.Key,
CopySource: aws.String(o.Bucket + "/" + o.Key),
// Perform a fake replacement of the metadata, otherwise AWS will
// reject the copy because nothing has changed. It doesn't notice
// that the "ContentType" and/or "CacheControl" have changed.
Metadata: metadata,
MetadataDirective: aws.String("REPLACE"),
}
}
func (o Object) log(format string, a ...interface{}) {
msg := fmt.Sprintf(format, a...)
info("%v: %s", o, msg)
}
func (o Object) calculateNewValue(header string, currentPtr *string) (bool, string) {
new := o.newHeaders[header]
current := ""
if currentPtr != nil {
current = *currentPtr
}
if new == "" {
o.log("the rules do not specify a change for %s", header)
return false, current
} else if new == current {
o.log("%s will remain \"%s\"", header, current)
return false, current
}
if current == "" {
o.log("%s will be set to \"%s\"", header, new)
} else {
o.log("%s will be updated from \"%s\" to \"%s\"", header, current, new)
}
return true, new
}
func (o Object) updateCopyObjectInput(head *s3.HeadObjectOutput, in *s3.CopyObjectInput) bool {
ccChange, ccNew := o.calculateNewValue("Cache-Control", head.CacheControl)
in.CacheControl = &ccNew
ctChange, ctNew := o.calculateNewValue("Content-Type", head.ContentType)
in.ContentType = &ctNew
return ccChange || ctChange
}
func (o *Object) queueRuleEffect(rule Rule) {
for _, when := range rule.When {
if endsWith(o.Key, when.Extension) {
o.newHeaders[rule.Header] = when.Then
return
}
}
o.newHeaders[rule.Header] = rule.Else
}