forked from lstoll/ftp2s3
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
220 lines (189 loc) · 5.05 KB
/
main.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
// An example FTP server build on top of go-raval. graval handles the details
// of the FTP protocol, we just provide a basic in-memory persistence driver.
//
// If you're looking to create a custom graval driver, this example is a
// reasonable starting point. I suggest copying this file and changing the
// function bodies as required.
//
// USAGE:
//
// go get github.com/yob/graval
// go install github.com/yob/graval/graval-mem
// ./bin/graval-mem
//
package main
import (
"errors"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strconv"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/yob/graval"
)
const (
fileOne = "This is the first file available for download.\n\nBy Jàmes"
fileTwo = "This is file number two.\n\n2012-12-04"
)
// A minimal driver for graval that stores everything in memory. The authentication
// details are fixed and the user is unable to upload, delete or rename any files.
//
// This really just exists as a minimal demonstration of the interface graval
// drivers are required to implement.
type S3Driver struct {
CacheDir string
UploadQueue chan string
Username string
Password string
}
func (driver *S3Driver) Authenticate(user string, pass string) bool {
return user == driver.Username && pass == driver.Password
}
func (driver *S3Driver) Bytes(path string) (bytes int) {
return -1
}
func (driver *S3Driver) ModifiedTime(path string) (time.Time, error) {
return time.Now(), nil
}
func (driver *S3Driver) ChangeDir(path string) bool {
return true
}
func (driver *S3Driver) DirContents(path string) (files []os.FileInfo) {
return []os.FileInfo{}
}
func (driver *S3Driver) DeleteDir(path string) bool {
return false
}
func (driver *S3Driver) DeleteFile(path string) bool {
return false
}
func (driver *S3Driver) Rename(fromPath string, toPath string) bool {
return false
}
func (driver *S3Driver) MakeDir(path string) bool {
return false
}
func (driver *S3Driver) GetFile(path string) (data string, err error) {
return "", errors.New("GET not supported")
}
func (driver *S3Driver) PutFile(destPath string, data io.Reader) bool {
// Write the file to the temp dir
fileName := filepath.Base(destPath)
filePath := filepath.Dir(destPath)
outDir := filepath.Join(driver.CacheDir, filePath)
outFile := filepath.Join(outDir, fileName)
if err := os.MkdirAll(outDir, 0755); err != nil {
log.Printf("Error making out dir %s\b", err)
return false
}
f, err := os.Create(outFile)
if err != nil {
log.Printf("Error creating out file %s\n", err)
return false
}
defer func() { f.Close() }()
log.Printf("Writing to %s\n", outFile)
if _, err := io.Copy(f, data); err != nil {
log.Printf("Error copying to file %s\n", err)
return false
}
// Message the s3 uploader to move it to S3.
select {
case driver.UploadQueue <- destPath:
default:
log.Printf("Queue full, skipping upload of %s\n", destPath)
}
return true
}
// graval requires a factory that will create a new driver instance for each
// client connection. Generally the factory will be fairly minimal. This is
// a good place to read any required config for your driver.
type S3DriverFactory struct {
CacheDir string
UploadQueue chan string
Username string
Password string
}
func (factory *S3DriverFactory) NewDriver() (graval.FTPDriver, error) {
return &S3Driver{
CacheDir: factory.CacheDir,
UploadQueue: factory.UploadQueue,
Username: factory.Username,
Password: factory.Password,
}, nil
}
// it's alive!
func main() {
cacheDir := os.Getenv("FTP2S3_CACHE_DIR")
if cacheDir == "" {
fmt.Println("Set FTP2S3_CACHE_DIR")
os.Exit(1)
}
bucket := os.Getenv("FTP2S3_BUCKET")
if bucket == "" {
fmt.Println("Set FTP2S3_BUCKET")
os.Exit(1)
}
prefix := os.Getenv("FTP2S3_PREFIX")
if prefix == "" {
fmt.Println("Set FTP2S3_PREFIX to the prefix you want to store in on S3")
os.Exit(1)
}
username := os.Getenv("FTP2S3_USERNAME")
if username == "" {
fmt.Println("Set FTP2S3_USERNAME")
os.Exit(1)
}
password := os.Getenv("FTP2S3_PASSWORD")
if password == "" {
fmt.Println("Set FTP2S3_PASSWORD")
os.Exit(1)
}
region := os.Getenv("FTP2S3_REGION")
if region == "" {
region = "us-east-1"
}
port := os.Getenv("FTP2S3_PORT")
if port == "" {
port = "2121"
}
portInt, err := strconv.Atoi(port)
if err != nil {
log.Fatal("Port must be numeric ", err)
}
fileQueue := make(chan string, 10000)
sess := session.New(&aws.Config{Region: aws.String(region)})
svc := s3.New(sess)
uploader := &S3Uploader{
S3: svc,
CacheDir: cacheDir,
S3Bucket: bucket,
S3Prefix: prefix,
}
go uploader.UploadLoop(fileQueue)
go func() {
uploader.Reconcile()
time.Sleep(30 * time.Minute)
}()
factory := &S3DriverFactory{
CacheDir: cacheDir,
UploadQueue: fileQueue,
Username: username,
Password: password,
}
ftpServer := graval.NewFTPServer(&graval.FTPServerOpts{
Factory: factory,
//Hostname: "0.0.0.0",
Port: portInt,
})
err = ftpServer.ListenAndServe()
if err != nil {
log.Print(err)
log.Fatal("Error starting server!")
}
}