· 5 years ago · Nov 26, 2020, 12:38 PM
1/*
2file: $GOPATH/src/goapi/main.go
3description: main.go is the initial bootstap to our goapi application, it handles
4- setting up the console
5- obtaining the config from the external config store
6- setting up the database connection
7- setting up the redis connection
8- applying which instances specified by the config will be served by the application
9Author: david@gofiliate.com
10Modified by: david:gofiliate.com
11Last Modified: 2020-11-23
12© 2020 Gotech Solutions Malta Limited (C72157), Trading as Gofiliate
13*/
14package main
15
16import (
17 "encoding/json"
18 "flag"
19 "fmt"
20 "github.com/gofiliate/godb" //custom module to handle our SQL connection and query dispatching
21 "os"
22 "internal/pkg/curl" // internal package that replicates a curl style request
23 goredis "internal/pkg/faux-redis" //helper package to mimic redis functionality
24 "internal/pkg/models/instances" //including our instances module as we need to know what we're serving
25
26)
27
28//Console flag for Authorization key used to retrieve config from config store
29var key string
30//Console flag config version to load into application. Example JSON below
31var config_version string
32/*{
33"base_domain": "api.gofiliate.localhost", //domain the API will be served from
34"base_port": 8080, //port api will listen on by default. Can be overridden [see below]
35"sql_read": { //connection details for READ SQL connection
36"host": "127.0.0.1",
37"port": 3306,
38"username": "goapi",
39"password": "password",
40"database": "goapi"
41},
42"sql_write": { //connection details for WRITE SQL connection
43"host": "127.0.0.1",
44"port": 3306,
45"username": "goapi",
46"password": "password",
47"database": "goapi"
48},
49"redis_read": { //connection details for READ Redis connection
50"host": "redis-cache.gofiliate.dev",
51"port": 6379,
52"password": "uXpM9U5pAsdYv2YR"
53},
54"redis_write": { //connection details for WRITE Redis connection
55"host": "redis-cache.gofiliate.dev",
56"port": 6379,
57"password": "uXpM9U5pAsdYv2YR"
58},
59"standalone_mode": true, //trigger to load API only for a specified brand(s)
60"idents": [ //list containing brand idents that the application will accept or refuse dependent on mode specified above
61"b2678bbe", //filtering done in middleware {APP_ROOT}/pkg/middleware/verifyHeaders.go
62"b8fd23a9",
63"c10f0baf"
64]
65}*/
66
67//Console flag for overriding listening port
68var override_port int
69//Console flag for initiating verbose mode [TODO]
70var v bool
71//Console flag for initiating testing mode [TODO]
72var t bool
73//Definition of where Config store is located
74const config_path string = "http://config.gofiliate.com/?version=%s"
75
76
77/*
78 TODO: remove this as its just a note
79 Authorization:2deb4e975098c0bec999db6a6c9d29ec
80*/
81
82//Struct to hold redis connection details [EXPORTABLE]
83type RedisDb struct {
84 Password string `json:"password"`
85 Host string `json:"host"`
86 Port int `json:"port"`
87}
88//Struct to hold Sql connection details [EXPORTABLE]
89type SqlDb struct {
90 Host string `json:"host"`
91 Port int `json:"port"`
92 User string `json:"username"`
93 Password string `json:"password"`
94 Database string `json:"database"`
95}
96//Struct to hold config information in same format as supply JSON from config store [EXPORTABLE]
97type Config struct {
98 Base_domain string `json:"base_domain"`
99 Base_port int `json:"base_port"`
100 DBR SqlDb `json:"sql_read"`
101 DBW SqlDb `json:"sql_write"`
102 RedR RedisDb `json:"redis_read"`
103 RedW RedisDb `json:"redis_write"`
104 Standalone bool `json:"standalone"`
105 Idents []string `json:idents`
106}
107///Our Application Config Variable [EXPORTABLE]
108var AppConfig Config
109//Our Instances Variable
110var instancesToServe []Instance
111
112type Instance struct {
113 Created string `json:"created"`
114 Instance_ident string `json:"ident"`
115 Instance_name string `json:"name"`
116}
117
118
119
120func main() {
121
122 //utter vainity
123 // GOAPI 2.0.1
124 //updateable from www.patorjk.com/software/taag
125 fmt.Printf("\n\n ________ ________ _____ __________.___ ________ _______ ____ \n / _____/ \\_____ \\ / _ \\\\______ \\ | \\_____ \\ \\ _ \\ /_ |\n/ \\ ___ / | \\ / /_\\ \\| ___/ | / ____/ / /_\\ \\ | |\n\\ \\_\\ \\/ | \\/ | \\ | | | / \\ \\ \\_/ \\ | |\n \\______ /\\_______ /\\____|__ /____| |___| \\_______ \\ /\\ \\_____ / /\\ |___|\n \\/ \\/ \\/ \\/ \\/ \\/ \\/ \n\n\n")
126 //end of vainity - for now
127
128 //setting up the -key flag
129 flag.StringVar(&key, "key", "", "-key {AUTHORIZATION_TOKEN}")
130 //setting up the -config console flag
131 flag.StringVar(&config_version, "config", "version-local-2.0.1", "-version {CONFIG VERSION}")
132 //setting up the -port console flag
133 flag.IntVar(&override_port, "port", 0, "-port {OVERRIDE_PORT")
134 //setting up the -v console flag
135 flag.BoolVar(&v, "v", false, "-v")
136 //setting up the -t console flag
137 flag.BoolVar(&t, "t", false, "-t")
138 //Read the flags into the application
139 flag.Parse()
140
141 //flag -key is MANDATORY to the starup of the application as config is guarded by an authorization header
142 if len(key) > 0 {
143
144 //piece together our config url
145 configUrl := fmt.Sprintf(config_path, config_version)
146
147 //create a map[string]string to hold header for use in curl.GET()
148 headers:= make(map[string]string)
149 headers["authorization"] = key
150 //Get our config from the config store
151 cfgJson, err := curl.Get(headers, configUrl)
152
153 //Success in retrieving the config is MANDATORY
154 if err != nil {
155 //inform the operator
156 fmt.Printf("[FATAL] Unable to successfully get json config reason: %s\n", err.Error())
157 //and quit
158 os.Exit(0)
159 } else {
160
161 //Load config JSON into our AppCfg variable
162 AppCfg, err := handleConfig([]byte(cfgJson))
163 //Success in loaded json into AppCfg is MANDATORY
164 if err != nil {
165 //inform operator
166 fmt.Printf("[FATAL] Cannot load config successfully from %s because: %v\n", configUrl, err.Error())
167 //and quit
168 os.Exit(0)
169 }
170
171 //load the READ credentials from the AppCfg into the Godb module
172 godb.LoadConfigExternal("read", AppCfg.DBR.User, AppCfg.DBR.Password, AppCfg.DBR.Host, AppCfg.DBR.Port, AppCfg.DBR.Database)
173 //load the WRITE credentials from the AppCfg into the Godb module
174 godb.LoadConfigExternal("write", AppCfg.DBW.User, AppCfg.DBW.Password, AppCfg.DBW.Host, AppCfg.DBW.Port, AppCfg.DBW.Database)
175
176 //Attempt a connection to the Read Database
177 err = godb.ConnectMysqlRead()
178
179 //Success in connecting to the ReadDB is MANDATORY
180 if err != nil {
181 //inform the operator
182 fmt.Printf("[FATAL] Cannot connect to READ Database %v\n", err.Error())
183 //and quit
184 os.Exit(0)
185 }
186
187 //Attempt a connection to the Write Database
188 err = godb.ConnectMysqlWrite()
189 //Success in connecting to the WriteDB is MANDATORY
190 if err != nil {
191 //inform the operator
192 fmt.Printf("[FATAL] Cannont connect to WRITE Database %v\n", err.Error())
193 //and quit
194 os.Exit(0)
195 }
196
197 /*
198 [TODO]: Implementation of actual redis connection
199 due to an error in go-redis currently (OTEL)
200 {GOPATH}/internal/pkg/fauxRedis pkg mimics functionality of goredis module
201 for seemless swapping
202 fauxRedis utilises a SQL key/value store to help development
203
204 */
205
206 //attempt to connect to the Read Redis Cache
207 err = goredis.ReadConnection(AppCfg.RedR.Host, AppCfg.RedR.Password, 0)
208 //Success in connecting to the Read Redis Cache is MANDATORY
209 if err != nil {
210 //inform the operator
211 fmt.Printf("[FATAL] Cannot connect to READ Redis Cache %v\n", err.Error())
212 //and quit
213 os.Exit(0)
214 }
215
216 //attempt to connect to the Write Redis Cache
217 err = goredis.WriteConnection(AppCfg.RedR.Host, AppCfg.RedR.Password, 0)
218 //Success in connecting to the Write Redis Cache is MANDATORY
219 if err != nil {
220 //inform the operator
221 fmt.Printf("[FATAL] Cannot connect to Write Redis Cache %v\n", err.Error())
222 //and quit
223 os.Exit(0)
224 }
225 //[TODO] REMOVE small redis test here
226
227 exists, err := goredis.KeyExists("test")
228 fmt.Printf("Exist is %T and %v\n", exists, exists)
229
230 if !exists {
231 fmt.Printf("The example key hasn't been set so we'll set it now...\n")
232 err := goredis.Set("test", "Well hello there david you fine fellow!", 2400)
233 if err != nil {
234 fmt.Printf("[Error] Cannot Set the redis key...\n")
235 }
236 }
237
238 value, err := goredis.Get("test")
239
240 if err != nil {
241 fmt.Printf("[Error] Cannot Get the redis key...\n")
242 }
243 fmt.Printf("Redis Key: %s\n", value)
244
245 //[TODO] REMOVE ENDOF small redis test here
246
247 // Check if service listener port has an override set.
248 // If so, and it is different to the base port, replace the base port with the override port.
249 port := AppCfg.Base_port
250 if override_port != 0 {
251
252 if override_port != AppCfg.Base_port {
253 port = override_port
254 }
255 }
256
257 //handle which instances we are going to be serving in the application
258 //if the application in standalone mode (only for certain instances)
259 //or common mode (serving all instances)
260
261 if AppCfg.Standalone {
262
263 //get our sql string for the instance info - used in console output and header verification
264 //this function specifies which instances to include if AppCfg.Ident is set
265 sql:= instances.GetInstancesInc(AppCfg.Idents)
266 query, err := godb.DBR.Prepare(sql)
267
268 //Success in getting the list of instances is mandatory
269 if err != nil {
270 //inform operator
271 fmt.Printf("[FATAL] Cannont retrieve instance list\n", err.Error())
272 //abd quit
273 os.Exit(0)
274 }
275
276 rows, err := query.Query()
277
278 //Success in getting the list of instances is still mandatory
279 if err != nil {
280 //inform operator
281 fmt.Printf("[FATAL] Cannont retrieve instance list\n", err.Error())
282 //abd quit
283 os.Exit(0)
284 }
285
286 for rows.Next() {
287 var instance Instance
288 rows.Scan(&instance.Created, &instance.Instance_ident, &instance.Instance_name)
289 instancesToServe = append(instancesToServe, instance)
290 }
291
292
293 } else {
294
295 //get our sql string for the instance info - used in console output and header verification
296 //this function specifies which instances to exclude if AppCfg.Ident is set
297 sql:= instances.GetInstancesEx(AppCfg.Idents)
298 query, err := godb.DBR.Prepare(sql)
299
300 //Success in getting the list of instances is mandatory
301 if err != nil {
302 //inform operator
303 fmt.Printf("[FATAL] Cannont retrieve instance list\n", err.Error())
304 //abd quit
305 os.Exit(0)
306 }
307
308 rows, err := query.Query()
309
310 //Success in getting the list of instances is still mandatory
311 if err != nil {
312 //inform operator
313 fmt.Printf("[FATAL] Cannont retrieve instance list\n", err.Error())
314 //abd quit
315 os.Exit(0)
316 }
317
318 for rows.Next() {
319 var instance Instance
320 rows.Scan(&instance.Created, &instance.Instance_ident, &instance.Instance_name)
321 instancesToServe = append(instancesToServe, instance)
322 }
323 fmt.Printf("%v\n", instancesToServe)
324
325 }
326
327
328
329 //setup the application routing. Handled in $GOAPTH/src/goapi/routing.go
330 GoAPIRoute(AppCfg.Base_domain, port)
331 }
332
333 //-key flag is MANDATORY
334 } else {
335 //Inform the operator
336 fmt.Println("[ERROR] API Key Not Supplied: Shutting Down")
337 //and quit
338 os.Exit(0)
339
340 }
341}
342
343//Simple function to handle unmarshaling the Application Config
344func handleConfig(body []byte)(*Config, error) {
345 cfg := new(Config)
346 err := json.Unmarshal(body, &cfg)
347 return cfg, err
348
349}
350
351
352