/
core.go
207 lines (178 loc) · 4.91 KB
/
core.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
package main
/*
* sql2mongo - A simple engine to take a SQL table, and dump it into a mongo collection
*
*/
import (
"encoding/json"
"flag"
"fmt"
"github.com/jmoiron/sqlx"
"io/ioutil"
"log"
"os"
"strings"
)
// our global debug object. Defaults to discard
var debugOut *log.Logger = log.New(ioutil.Discard, "", log.Lshortfile)
// If you add a new SQL input, make sure to add to this list: sqlList[name] = yourSqlInitFunction
// see mysql.go or pgsql.go
var sqlList map[string]func(*Credential, string) (*sqlx.DB, *sqlx.Rows)
func init() {
sqlList = make(map[string]func(*Credential, string) (*sqlx.DB, *sqlx.Rows))
}
type Config struct {
Jobs []Job
}
func (c *Config) GetJob(name string) (*Job, bool) {
for _, job := range c.Jobs {
if name == job.Name && job.Enabled == true {
return &job, true
}
}
// sad face
return nil, false
}
type Job struct {
Name string
Description string
Condition string
Enabled bool
WriteOperation string
SqlType string
SqlColumns string
Mongo Credential
SQL Credential
}
type Credential struct {
Name string
Host string
Username string
Password string
Database string
Table string
}
func main() {
var (
debug bool
confFile string
configTest bool
jobName string
listJobs bool
dontByte bool
dont_id bool
)
flag.BoolVar(&debug, "debug", false, "Enable Debug output")
flag.StringVar(&confFile, "config", "my2mo.json", "Config file to read")
flag.BoolVar(&configTest, "configtest", false, "Load and parse configs, and exit")
flag.StringVar(&jobName, "job", "", "Name of the job to run")
flag.BoolVar(&dontByte, "dontconvertbytes", false, "We automatically convert byte arrays to strings. Set this to prevent it.")
flag.BoolVar(&dont_id, "dontconvertid", false, "We automatically convert any SQL column 'id' to mongo element '_id'. Set this to prevent it.")
flag.BoolVar(&listJobs, "list", false, "List the jobs available")
flag.Parse()
if debug {
debugOut = log.New(os.Stdout, "[DEBUG]", log.Lshortfile)
}
// Because of my silly logic, the config params are logically
// inverse from the function parameters. Dealing with that here.
var (
convertBytes bool = true
convertId bool = true
)
if dontByte {
convertBytes = false
}
if dont_id {
convertId = false
}
// Read config
conf := loadFile(confFile)
if configTest {
// Just kicking the tires...
fmt.Println("Config loaded and bootstrapped successfully...")
os.Exit(0)
}
if listJobs {
for _, job := range conf.Jobs {
if job.Enabled == true {
fmt.Printf("%s: %s\n", job.Name, job.Description)
}
}
os.Exit(0)
} else if jobName == "" {
log.Fatal("--job is required")
}
// Grab the job, and let us get this party started
job, ok := conf.GetJob(jobName)
if ok == false {
log.Fatalf("Job name %s is not valid, or disabled. Perhaps --list?\n", jobName)
} else {
// ensure writeoperation is one of insert, update, upsert
if job.WriteOperation == "insert" || job.WriteOperation == "update" || job.WriteOperation == "upsert" {
} else {
log.Fatalf("WriteOperation of %s for job %s is invalid!\n", job.WriteOperation, job.Name)
}
// Mongo handle, right to the collection
mongod, mongoc := mongoOpen(&job.Mongo)
defer mongod.Close()
// SQL can vary
var sqlc *sqlx.Rows
var sqld *sqlx.DB
// Test the SqlType
found := false
for k, f := range sqlList {
if k == job.SqlType {
found = true
debugOut.Printf("Found SQL type %s\n", k)
sqld, sqlc = f(&job.SQL, job.SqlColumns)
break
}
}
if found == false {
log.Fatalf("SQL type of %s is not currently valid. Pick one of: "+joinKeys(sqlList)+"\n", job.SqlType)
}
defer sqld.Close()
writeCount := 0
errorCount := 0
// Let's do this
for sqlc.Next() {
res, err := sqlGetRow(sqlc, convertBytes, convertId)
// we already outputted any error in sqlGetRow
// so we'll just skip this write, and move along
if err == nil {
ok := mongoWriteRow(mongoc, job.WriteOperation, res)
if ok {
writeCount++
} else {
errorCount++
}
} else {
errorCount++
}
}
totalCount := writeCount + errorCount
log.Printf("Load completed with %d/%d rows written", writeCount, totalCount)
}
}
// joinKeys is a simple function to spit out a comma-separated list of keys from
// specifically the sqlList map
func joinKeys(m map[string]func(*Credential,string) (*sqlx.DB, *sqlx.Rows)) string {
var keys []string
for k, _ := range m {
keys = append(keys, k)
}
return strings.Join(keys, ", ")
}
// loadFile loads the specified file, and marshals the JSON into a Config struct
func loadFile(filePath string) Config {
var conf Config
buf, err := ioutil.ReadFile(filePath)
if err != nil {
log.Fatalf("Error reading config file '%s': %s\n", filePath, err)
}
err = json.Unmarshal(buf, &conf)
if err != nil {
log.Fatalf("Error parsing JSON in config file '%s': %s\n", filePath, err)
}
return conf
}