/
s3util.go
130 lines (112 loc) · 3.45 KB
/
s3util.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
130
package common
import (
"errors"
"net/url"
"strconv"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
)
// Exported error messages
var (
ErrS3MissingBucketKey = errors.New("missing S3 bucket or object key")
ErrS3UnknownScheme = errors.New("unknown URI scheme")
)
// s3Info contains the information needed to connect to an S3
// datastore and create or retrieve objects
type s3Info struct {
awsConfig *aws.Config
bucket string
key string
}
// isS3 does a very basic check if the given string *could* be an S3 URI
func isS3(s string) bool {
return strings.HasPrefix(s, "s3://") || strings.HasPrefix(s, "s3n://")
}
// parseS3URI returns a struct containing all the information needed to connect
// to an S3 endpoing and create or retrieve objects. The data is collected from
// parsing the passed s3uri and environment variables.
func parseS3URI(s3uri string) (*s3Info, error) {
var info *s3Info // parsed info
var u *url.URL // parsed url
var accessKey, secretKey string // key holders
var err error // general error holder
// The `net/url` package does not handle '/' in password.
// Therefore, we strip out and parse the user/password portion manually.
// See: https://github.com/myENA/consul-backinator/issues/30
if strings.Contains(s3uri, "@") {
var keyStart = strings.Index(s3uri, "://") + 3 // get start
var keyEnd = strings.Index(s3uri, "@") // get end
var keyString = s3uri[keyStart:keyEnd] // pull out key string
// check key string
if strings.Contains(keyString, ":") {
// split the keys
var keySplit = strings.Split(keyString, ":")
// check split
if len(keySplit) == 2 {
// set keys
accessKey = keySplit[0]
secretKey = keySplit[1]
// rewrite uri - remove credentials
s3uri = s3uri[:keyStart] + s3uri[keyEnd+1:]
}
}
}
// parse the s3 path
if u, err = url.Parse(s3uri); err != nil {
return nil, err
}
// check scheme for giggles
if u.Scheme != "s3" && u.Scheme != "s3n" {
return nil, ErrS3UnknownScheme
}
// init info
info = &s3Info{awsConfig: aws.NewConfig()}
// check access/secret key
if accessKey != "" && secretKey != "" {
info.awsConfig.Credentials = credentials.NewStaticCredentials(
accessKey,
secretKey,
"",
)
}
// get region
if temps := u.Query().Get("region"); temps != "" {
info.awsConfig.Region = aws.String(temps)
}
// get bucket
if info.bucket = u.Host; info.bucket == "" {
return nil, ErrS3MissingBucketKey
}
// get object key
if info.key = u.Path; info.key == "" || info.key == "/" {
return nil, ErrS3MissingBucketKey
}
// check for endpoint override
if temps := u.Query().Get("endpoint"); temps != "" {
info.awsConfig.Endpoint = aws.String(temps)
}
// check for ssl override
if temps := u.Query().Get("secure"); temps != "" {
var secure bool // local bool
if secure, err = strconv.ParseBool(temps); err != nil {
return nil, err
}
// update config to disable SSL if secure is false
// see #43 - this was a terrible name for this paramater
if secure == false {
info.awsConfig.DisableSSL = aws.Bool(true)
}
}
//check for pathstyle override
if temps := u.Query().Get("pathstyle"); temps != "" {
var pathstyle bool // local bool
if pathstyle, err = strconv.ParseBool(temps); err != nil {
return nil, err
}
// update config
info.awsConfig.S3ForcePathStyle = aws.Bool(pathstyle)
}
// return populated struct
return info, nil
}