1 package awss3
2
3 import (
4 "fmt"
5 "io"
6 "net/url"
7 "os"
8 "path"
9 "strings"
10
11 "github.com/aws/aws-sdk-go/aws"
12 "github.com/aws/aws-sdk-go/aws/session"
13 "github.com/aws/aws-sdk-go/service/s3"
14 "github.com/aws/aws-sdk-go/service/s3/s3iface"
15 "github.com/golang-migrate/migrate/v4/source"
16 )
17
18 func init() {
19 source.Register("s3", &s3Driver{})
20 }
21
22 type s3Driver struct {
23 s3client s3iface.S3API
24 config *Config
25 migrations *source.Migrations
26 }
27
28 type Config struct {
29 Bucket string
30 Prefix string
31 }
32
33 func (s *s3Driver) Open(folder string) (source.Driver, error) {
34 config, err := parseURI(folder)
35 if err != nil {
36 return nil, err
37 }
38
39 sess, err := session.NewSession()
40 if err != nil {
41 return nil, err
42 }
43
44 return WithInstance(s3.New(sess), config)
45 }
46
47 func WithInstance(s3client s3iface.S3API, config *Config) (source.Driver, error) {
48 driver := &s3Driver{
49 config: config,
50 s3client: s3client,
51 migrations: source.NewMigrations(),
52 }
53
54 if err := driver.loadMigrations(); err != nil {
55 return nil, err
56 }
57
58 return driver, nil
59 }
60
61 func parseURI(uri string) (*Config, error) {
62 u, err := url.Parse(uri)
63 if err != nil {
64 return nil, err
65 }
66
67 prefix := strings.Trim(u.Path, "/")
68 if prefix != "" {
69 prefix += "/"
70 }
71
72 return &Config{
73 Bucket: u.Host,
74 Prefix: prefix,
75 }, nil
76 }
77
78 func (s *s3Driver) loadMigrations() error {
79 output, err := s.s3client.ListObjects(&s3.ListObjectsInput{
80 Bucket: aws.String(s.config.Bucket),
81 Prefix: aws.String(s.config.Prefix),
82 Delimiter: aws.String("/"),
83 })
84 if err != nil {
85 return err
86 }
87 for _, object := range output.Contents {
88 _, fileName := path.Split(aws.StringValue(object.Key))
89 m, err := source.DefaultParse(fileName)
90 if err != nil {
91 continue
92 }
93 if !s.migrations.Append(m) {
94 return fmt.Errorf("unable to parse file %v", aws.StringValue(object.Key))
95 }
96 }
97 return nil
98 }
99
100 func (s *s3Driver) Close() error {
101 return nil
102 }
103
104 func (s *s3Driver) First() (uint, error) {
105 v, ok := s.migrations.First()
106 if !ok {
107 return 0, os.ErrNotExist
108 }
109 return v, nil
110 }
111
112 func (s *s3Driver) Prev(version uint) (uint, error) {
113 v, ok := s.migrations.Prev(version)
114 if !ok {
115 return 0, os.ErrNotExist
116 }
117 return v, nil
118 }
119
120 func (s *s3Driver) Next(version uint) (uint, error) {
121 v, ok := s.migrations.Next(version)
122 if !ok {
123 return 0, os.ErrNotExist
124 }
125 return v, nil
126 }
127
128 func (s *s3Driver) ReadUp(version uint) (io.ReadCloser, string, error) {
129 if m, ok := s.migrations.Up(version); ok {
130 return s.open(m)
131 }
132 return nil, "", os.ErrNotExist
133 }
134
135 func (s *s3Driver) ReadDown(version uint) (io.ReadCloser, string, error) {
136 if m, ok := s.migrations.Down(version); ok {
137 return s.open(m)
138 }
139 return nil, "", os.ErrNotExist
140 }
141
142 func (s *s3Driver) open(m *source.Migration) (io.ReadCloser, string, error) {
143 key := path.Join(s.config.Prefix, m.Raw)
144 object, err := s.s3client.GetObject(&s3.GetObjectInput{
145 Bucket: aws.String(s.config.Bucket),
146 Key: aws.String(key),
147 })
148 if err != nil {
149 return nil, "", err
150 }
151 return object.Body, m.Identifier, nil
152 }
153
View as plain text