Skip to content

Commit 45e71b9

Browse files
committed
first commit
1 parent 819f435 commit 45e71b9

14 files changed

Lines changed: 3096 additions & 9 deletions

.gitignore

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Binaries for programs and plugins
2+
*.exe
3+
*.exe~
4+
*.dll
5+
*.so
6+
*.dylib
7+
8+
# Test binary, built with `go test -c`
9+
*.test
10+
11+
# Output of the go coverage tool, specifically when used with LiteIDE
12+
*.out
13+
14+
config
15+
16+
.DS_Store
17+
.idea
18+
dthcli
19+
20+

Dockerfile

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
FROM amazonlinux:latest as builder
2+
3+
RUN yum update && yum install -y tar gzip
4+
5+
RUN cd /tmp && \
6+
curl -LO https://golang.org/dl/go1.16.4.linux-amd64.tar.gz && \
7+
rm -rf /usr/local/go && \
8+
tar -C /usr/local -xzf /tmp/go1.16.4.linux-amd64.tar.gz
9+
10+
WORKDIR /build
11+
COPY . .
12+
RUN PATH=$PATH:/usr/local/go/bin GOPROXY=https://goproxy.io,direct GOOS=linux go build -o dthcli .
13+
14+
FROM amazonlinux:latest
15+
16+
ENV SOURCE_TYPE Amazon_S3
17+
18+
ENV JOB_TABLE_NAME ''
19+
ENV JOB_QUEUE_NAME ''
20+
21+
ENV SRC_BUCKET ''
22+
ENV SRC_PREFIX ''
23+
ENV SRC_REGION ''
24+
ENV SRC_ENDPOINT ''
25+
ENV SRC_CREDENTIALS ''
26+
ENV SRC_IN_CURRENT_ACCOUNT false
27+
28+
ENV DEST_BUCKET ''
29+
ENV DEST_PREFIX ''
30+
ENV DEST_REGION ''
31+
ENV DEST_CREDENTIALS ''
32+
ENV DEST_IN_CURRENT_ACCOUNT false
33+
34+
ENV MAX_KEYS 1000
35+
ENV CHUNK_SIZE 5
36+
ENV MULTIPART_THRESHOLD 10
37+
ENV MESSAGE_BATCH_SIZE 10
38+
ENV FINDER_DEPTH 0
39+
ENV FINDER_NUMBER 1
40+
ENV WORKER_NUMBER 4
41+
42+
WORKDIR /app
43+
RUN touch config.yaml
44+
COPY --from=builder /build/dthcli .
45+
ENTRYPOINT ["/app/dthcli", "run"]

README.md

Lines changed: 75 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,83 @@
1-
## My Project
1+
# data-transfer-hub-cli
22

3-
TODO: Fill this README out!
3+
dthcli (short for data-transfer-hub-cli) is a distributed tool to transfer data to Amazon S3 from other cloud storage services (including Aliyun OSS, Tencent COS, Qiniu Kodo, etc.) or between AWS regions (cross partition).
44

5-
Be sure to:
5+
## Introduction
66

7-
* Change the title in this README
8-
* Edit your repository description on GitHub
7+
This tool leverages Amazon SQS to distribute the transfer processes in many worker nodes. You can run as many worker nodes as required concurrently for large volume of objects. Each worker node will consume the messages from Amazon SQS and start transferring from the source to destination. Each message contains information that represents a object in cloud storage service to be transfered.
98

10-
## Security
119

12-
See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.
10+
## Installation
1311

14-
## License
12+
You can clone this repo and build by yourself (Go Version >= v1.16)
1513

16-
This project is licensed under the Apache-2.0 License.
1714

15+
## Prerequisites
16+
17+
You can run this tool in any places, even in local. However, you will need to have a SQS Queue and a DynamoDB table created before using the tool. DynamoDB is used to store the replication status of each objects, the partition key for DynamoDB must be `ObjectKey`
18+
19+
If you need to provide AK/SK to accessing cloud service, you will need to set up a credential secure string in Amazon Secrets Manager with a format as below
20+
21+
```
22+
{
23+
"access_key_id": "<Your Access Key ID>",
24+
"secret_access_key": "<Your Access Key Secret>"
25+
}
26+
```
27+
28+
## Configuration
29+
30+
The job information such as Source Bucket, Destination Bucket etc. are provided in either a config file or via environment variables.
31+
32+
An example of full config file can be found [here](./config-example.yaml)
33+
34+
You must provide at least the minimum information as below:
35+
```yaml
36+
srcBucket: src-bucket
37+
srcRegion: us-west-2
38+
39+
destBucket: dest-bucket
40+
destRegion: cn-north-1
41+
42+
jobTableName: test-table
43+
jobQueueName: test-queue
44+
```
45+
46+
By default, this tool will try to read a `config.yaml` in the same folder, if you create the configuration file in a different folder or with a different file name, please use extra option `--config xxx.yaml` to load your config file.
47+
48+
49+
## Usage
50+
51+
Run `dthcli help` for more details.
52+
```
53+
$ ./dthcli help
54+
A distributed CLI to replicate data to Amazon S3 from other cloud storage services.
55+
56+
Usage:
57+
dthcli [command]
58+
59+
Available Commands:
60+
help Help about any command
61+
run Start running a job
62+
version Print the version number
63+
64+
Flags:
65+
--config string config file (default is ./config.yaml)
66+
-h, --help help for dthcli
67+
68+
Use "dthcli [command] --help" for more information about a command.
69+
```
70+
71+
To actually start the job, use `dthcli run` command.
72+
73+
- Start Finder Job
74+
75+
```
76+
./dthcli run -t Finder
77+
```
78+
79+
- Start Worker Job
80+
81+
```
82+
./dthcli run -t Worker
83+
```

cmd/root.go

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
/*
2+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cmd
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"log"
23+
"os"
24+
25+
"github.com/spf13/cobra"
26+
"github.com/spf13/viper"
27+
"golang.a2z.com/dthcli/dth"
28+
)
29+
30+
// Version Number
31+
const Version = "v1.0.0"
32+
33+
var (
34+
cfgFile, jobType string
35+
cfg *dth.JobConfig
36+
)
37+
38+
// rootCmd represents the base command when called without any subcommands
39+
var rootCmd = &cobra.Command{
40+
Use: "dthcli",
41+
Short: "A distributed CLI to transfer data to Amazon S3",
42+
Long: `A distributed CLI to transfer data to Amazon S3 from other cloud storage services.`,
43+
44+
// Uncomment the following line if your bare application
45+
// has an action associated with it:
46+
// Run: func(cmd *cobra.Command, args []string) { },
47+
}
48+
49+
// Execute adds all child commands to the root command and sets flags appropriately.
50+
// This is called by main.main(). It only needs to happen once to the rootCmd.
51+
func Execute() {
52+
if err := rootCmd.Execute(); err != nil {
53+
fmt.Println(err)
54+
os.Exit(1)
55+
}
56+
}
57+
58+
func init() {
59+
cobra.OnInitialize(initConfig)
60+
61+
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is ./config.yaml)")
62+
63+
runCmd.Flags().StringVarP(&jobType, "type", "t", "Finder", "Job Type, choose either Finder or Worker")
64+
65+
rootCmd.AddCommand(versionCmd)
66+
rootCmd.AddCommand(runCmd)
67+
}
68+
69+
// initConfig reads in config file and ENV variables if set.
70+
func initConfig() {
71+
viper.SetDefault("srcType", "Amazon_S3")
72+
viper.SetDefault("destStorageClass", "STANDARD")
73+
viper.SetDefault("srcPrefix", "")
74+
viper.SetDefault("srcCredential", "")
75+
viper.SetDefault("srcEndpoint", "")
76+
viper.SetDefault("destPrefix", "")
77+
viper.SetDefault("destCredential", "")
78+
viper.SetDefault("destAcl", "bucket-owner-full-control")
79+
80+
viper.SetDefault("options.chunkSize", dth.DefaultChunkSize)
81+
viper.SetDefault("options.multipartThreshold", dth.DefaultMultipartThreshold)
82+
viper.SetDefault("options.maxKeys", dth.DefaultMaxKeys)
83+
viper.SetDefault("options.messageBatchSize", dth.DefaultMessageBatchSize)
84+
viper.SetDefault("options.finderDepth", dth.DefaultFinderDepth)
85+
viper.SetDefault("options.finderNumber", dth.DefaultFinderNumber)
86+
viper.SetDefault("options.workerNumber", dth.DefaultWorkerNumber)
87+
viper.SetDefault("options.includeMetadata", false)
88+
89+
viper.BindEnv("srcType", "SOURCE_TYPE")
90+
viper.BindEnv("srcBucket", "SRC_BUCKET")
91+
viper.BindEnv("srcPrefix", "SRC_PREFIX")
92+
viper.BindEnv("srcRegion", "SRC_REGION")
93+
viper.BindEnv("srcEndpoint", "SRC_ENDPOINT")
94+
viper.BindEnv("srcCredential", "SRC_CREDENTIALS")
95+
viper.BindEnv("SrcInCurrentAccount", "SRC_IN_CURRENT_ACCOUNT")
96+
97+
viper.BindEnv("destBucket", "DEST_BUCKET")
98+
viper.BindEnv("destPrefix", "DEST_PREFIX")
99+
viper.BindEnv("destRegion", "DEST_REGION")
100+
viper.BindEnv("destCredential", "DEST_CREDENTIALS")
101+
viper.BindEnv("destInCurrentAccount", "DEST_IN_CURRENT_ACCOUNT")
102+
viper.BindEnv("destStorageClass", "DEST_STORAGE_CLASS")
103+
viper.BindEnv("destAcl", "DEST_ACL")
104+
105+
viper.BindEnv("jobTableName", "JOB_TABLE_NAME")
106+
viper.BindEnv("jobQueueName", "JOB_QUEUE_NAME")
107+
108+
viper.BindEnv("options.maxKeys", "MAX_KEYS")
109+
viper.BindEnv("options.chunkSize", "CHUNK_SIZE")
110+
viper.BindEnv("options.multipartThreshold", "MULTIPART_THRESHOLD")
111+
viper.BindEnv("options.messageBatchSize", "MESSAGE_BATCH_SIZE")
112+
viper.BindEnv("options.finderDepth", "FINDER_DEPTH")
113+
viper.BindEnv("options.finderNumber", "FINDER_NUMBER")
114+
viper.BindEnv("options.workerNumber", "WORKER_NUMBER")
115+
viper.BindEnv("options.includeMetadata", "INCLUDE_METADATA")
116+
117+
if cfgFile != "" {
118+
// Use config file from the flag.
119+
viper.SetConfigFile(cfgFile)
120+
} else {
121+
// Default config file is "./config.yaml"
122+
viper.AddConfigPath(".")
123+
viper.SetConfigName("config")
124+
}
125+
126+
viper.AutomaticEnv() // read in environment variables that match
127+
128+
// If a config file is found, read it in.
129+
viper.ReadInConfig()
130+
if err := viper.ReadInConfig(); err == nil {
131+
log.Println("Using config file:", viper.ConfigFileUsed())
132+
}
133+
134+
options := &dth.JobOptions{
135+
ChunkSize: viper.GetInt("options.chunkSize"),
136+
MultipartThreshold: viper.GetInt("options.multipartThreshold"),
137+
MaxKeys: viper.GetInt32("options.maxKeys"),
138+
MessageBatchSize: viper.GetInt("options.messageBatchSize"),
139+
FinderDepth: viper.GetInt("options.finderDepth"),
140+
FinderNumber: viper.GetInt("options.finderNumber"),
141+
WorkerNumber: viper.GetInt("options.workerNumber"),
142+
IncludeMetadata: viper.GetBool("options.includeMetadata"),
143+
}
144+
145+
cfg = &dth.JobConfig{
146+
SrcType: viper.GetString("srcType"),
147+
SrcBucket: viper.GetString("srcBucket"),
148+
SrcPrefix: viper.GetString("srcPrefix"),
149+
SrcRegion: viper.GetString("srcRegion"),
150+
SrcEndpoint: viper.GetString("srcEndpoint"),
151+
SrcCredential: viper.GetString("srcCredential"),
152+
SrcInCurrentAccount: viper.GetBool("srcInCurrentAccount"),
153+
DestBucket: viper.GetString("destBucket"),
154+
DestPrefix: viper.GetString("destPrefix"),
155+
DestRegion: viper.GetString("destRegion"),
156+
DestCredential: viper.GetString("destCredential"),
157+
DestStorageClass: viper.GetString("destStorageClass"),
158+
DestAcl: viper.GetString("destAcl"),
159+
DestInCurrentAccount: viper.GetBool("destInCurrentAccount"),
160+
JobTableName: viper.GetString("jobTableName"),
161+
JobQueueName: viper.GetString("jobQueueName"),
162+
JobOptions: options,
163+
}
164+
165+
}
166+
167+
var versionCmd = &cobra.Command{
168+
Use: "version",
169+
Short: "Print the version number",
170+
Long: "Print the version number",
171+
Run: func(cmd *cobra.Command, args []string) {
172+
fmt.Printf("dthcli version %s\n", Version)
173+
},
174+
}
175+
176+
var runCmd = &cobra.Command{
177+
Use: "run",
178+
Short: "Start running a job",
179+
Long: `Start running a job.
180+
181+
For example: dthcli run -t Finder
182+
183+
Supported types:
184+
- Finder: Finder is a job that lists and compares objects in the source and target buckets, and sends the delta list to SQS Queue.
185+
- Worker: Worker is a job that consumes the messages from SQS Queue and start the migration
186+
`,
187+
Run: func(cmd *cobra.Command, args []string) {
188+
189+
if cfg.SrcBucket == "" || cfg.DestBucket == "" {
190+
log.Fatalf("Cannot find source or destination bucket name, please check if you have run with a config file or environment variables. Run `dthcli help` for more details")
191+
}
192+
193+
log.Printf("Start running %s job", jobType)
194+
ctx := context.Background()
195+
196+
var job dth.Job
197+
198+
switch jobType {
199+
case "Finder":
200+
job = dth.NewFinder(ctx, cfg)
201+
202+
case "Worker":
203+
job = dth.NewWorker(ctx, cfg)
204+
205+
default:
206+
log.Fatalf("Unknown Job Type - %s. Type must be either Finder or Worker\n, please start again", jobType)
207+
208+
}
209+
job.Run(ctx)
210+
},
211+
}

0 commit comments

Comments
 (0)