diff --git a/build/cicd/internal/config/config.go b/build/cicd/internal/config/config.go index 87b0419..c7f62f9 100644 --- a/build/cicd/internal/config/config.go +++ b/build/cicd/internal/config/config.go @@ -3,6 +3,7 @@ package config import ( "context" "fmt" + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext" "log" "os" "path/filepath" @@ -33,13 +34,13 @@ const ( type Env = string var ( - EnvDev Env = "dev" - EnvStage Env = "stage" - EnvProd Env = "prod" + EnvDev Env = webcontext.Env_Dev + EnvStage Env = webcontext.Env_Stage + EnvProd Env = webcontext.Env_Prod ) // List of env names used by main.go for help. -var EnvNames = []Function{ +var EnvNames = []Env{ EnvDev, EnvStage, EnvProd, diff --git a/go.mod b/go.mod index a62a16a..5aa59e5 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/tinylib/msgp v1.1.0 // indirect github.com/urfave/cli v1.21.0 github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 - gitlab.com/geeks-accelerator/oss/devops v1.0.0 + gitlab.com/geeks-accelerator/oss/devops v1.0.2 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 // indirect diff --git a/go.sum b/go.sum index 38f9057..4f7f887 100644 --- a/go.sum +++ b/go.sum @@ -311,6 +311,8 @@ gitlab.com/geeks-accelerator/oss/devops v0.0.0-20190822001238-2bc33b036611 h1:uv gitlab.com/geeks-accelerator/oss/devops v0.0.0-20190822001238-2bc33b036611/go.mod h1:rvI71qNJyNiO99ZgGnv/PmJCVrjJjupsXBmfYFXdjGM= gitlab.com/geeks-accelerator/oss/devops v1.0.0 h1:5XMS1NO34ZJbrSN8/yOwukCP9NeuDY1JLMyWr5bwzng= gitlab.com/geeks-accelerator/oss/devops v1.0.0/go.mod h1:rvI71qNJyNiO99ZgGnv/PmJCVrjJjupsXBmfYFXdjGM= +gitlab.com/geeks-accelerator/oss/devops v1.0.2 h1:LqME1zTc9bgB+J/tw7tv1WDjvgmrgl2OZdKcRToleqg= +gitlab.com/geeks-accelerator/oss/devops v1.0.2/go.mod h1:rvI71qNJyNiO99ZgGnv/PmJCVrjJjupsXBmfYFXdjGM= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/internal/platform/web/webcontext/context.go b/internal/platform/web/webcontext/context.go index 3117936..70b86c0 100644 --- a/internal/platform/web/webcontext/context.go +++ b/internal/platform/web/webcontext/context.go @@ -45,6 +45,13 @@ var ( Env_Prod Env = "prod" ) +// List of env names. +var EnvNames = []Env{ + Env_Dev, + Env_Stage, + Env_Prod, +} + func ContextEnv(ctx context.Context) string { cv := ctx.Value(KeyValues).(*Values) if cv != nil { diff --git a/tools/schema/main.go b/tools/schema/main.go index 0b7f0ab..ed6227f 100644 --- a/tools/schema/main.go +++ b/tools/schema/main.go @@ -2,26 +2,22 @@ package main import ( "context" - "encoding/json" - "expvar" - "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext" + "fmt" "log" "net/url" "os" + "strings" "time" - "geeks-accelerator/oss/saas-starter-kit/internal/platform/flag" + "github.com/urfave/cli" + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext" "geeks-accelerator/oss/saas-starter-kit/internal/schema" - "github.com/kelseyhightower/envconfig" "github.com/lib/pq" _ "github.com/lib/pq" sqltrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql" sqlxtrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/jmoiron/sqlx" ) -// build is the git version of this program. It is set using build flags in the makefile. -var build = "develop" - // service is the name of the program used for logging, tracing and the // the prefix used for loading env variables // ie: export SCHEMA_ENV=dev @@ -38,57 +34,101 @@ type DB struct { } func main() { + // ========================================================================= // Logging - - log := log.New(os.Stdout, service+" : ", log.LstdFlags|log.Lmicroseconds|log.Lshortfile) + log.SetFlags(log.LstdFlags | log.Lmicroseconds | log.Lshortfile) + log.SetPrefix(service + " : ") + log := log.New(os.Stdout, log.Prefix(), log.Flags()) // ========================================================================= - // Configuration - var cfg struct { - Env string `default:"dev" envconfig:"ENV"` - DB struct { - Host string `default:"127.0.0.1:5433" envconfig:"HOST"` - User string `default:"postgres" envconfig:"USER"` - Pass string `default:"postgres" envconfig:"PASS" json:"-"` // don't print - Database string `default:"shared" envconfig:"DATABASE"` - Driver string `default:"postgres" envconfig:"DRIVER"` - Timezone string `default:"utc" envconfig:"TIMEZONE"` - DisableTLS bool `default:"true" envconfig:"DISABLE_TLS"` - } + // New CLI application. + app := cli.NewApp() + app.Name = "schema" + app.Version = "1.0.0" + app.Author = "Lee Brown" + app.Email = "lee@geeksinthewoods.com" + + app.Commands = []cli.Command{ + { + Name: "migrate", + Aliases: []string{"m"}, + Usage: "run schema migration", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "env", + Usage: fmt.Sprintf("target environment, one of [%s]", + strings.Join(webcontext.EnvNames, ", ")), + Required: true, + EnvVar: "ENV", + }, + cli.StringFlag{ + Name: "host", + Usage: "host", + Required: true, + Value:"127.0.0.1:5433", + EnvVar: "SCHEMA_DB_HOST", + }, + cli.StringFlag{ + Name: "user", + Usage: "username", + Required: true, + Value: "postgres", + EnvVar: "SCHEMA_DB_USER", + }, + cli.StringFlag{ + Name: "pass", + Usage: "password", + Required: true, + Value: "postgres", + EnvVar: "SCHEMA_DB_PASS", + }, + cli.StringFlag{ + Name: "database", + Usage: "name of the default", + Required: true, + Value: "shared", + EnvVar: "SCHEMA_DB_DATABASE", + }, + cli.StringFlag{ + Name: "driver", + Usage: "database drive to use for connection", + Required: true, + Value: "postgres", + EnvVar: "SCHEMA_DB_DRIVER", + }, + cli.BoolTFlag{ + Name: "disable-tls", + Usage: "disable TLS for the database connection", + EnvVar: "SCHEMA_DB_DISABLE_TLS", + }, + }, + Action: func(c *cli.Context) error { + targetEnv := c.String("env") + var dbInfo = DB { + Host : c.String("host"), + User : c.String("user"), + Pass : c.String("pass"), + Database : c.String("database"), + + Driver : c.String("driver"), + DisableTLS: c.Bool("disable-tls"), + } + + return runMigrate(log, targetEnv, dbInfo) + }, + }, } - // For additional details refer to https://github.com/kelseyhightower/envconfig - if err := envconfig.Process(service, &cfg); err != nil { - log.Fatalf("main : Parsing Config : %v", err) + err := app.Run(os.Args) + if err != nil { + log.Fatalf("%+v", err) } - if err := flag.Process(&cfg); err != nil { - if err != flag.ErrHelp { - log.Fatalf("main : Parsing Command Line : %v", err) - } - return // We displayed help. - } - - // ========================================================================= - // Log App Info - - // Print the build version for our logs. Also expose it under /debug/vars. - expvar.NewString("build").Set(build) - log.Printf("main : Started : Application Initializing version %q", build) - defer log.Println("main : Completed") - - // Print the config for our logs. It's important to any credentials in the config - // that could expose a security risk are excluded from being json encoded by - // applying the tag `json:"-"` to the struct var. - { - cfgJSON, err := json.MarshalIndent(cfg, "", " ") - if err != nil { - log.Fatalf("main : Marshalling Config to JSON : %v", err) - } - log.Printf("main : Config : %v\n", string(cfgJSON)) - } +} +// runMigrate executes the schema migration against the provided database connection details. +func runMigrate(log *log.Logger, targetEnv string, dbInfo DB) error { // ========================================================================= // Start Database var dbUrl url.URL @@ -97,20 +137,18 @@ func main() { var q url.Values = make(map[string][]string) // Handle SSL Mode - if cfg.DB.DisableTLS { + if dbInfo.DisableTLS { q.Set("sslmode", "disable") } else { q.Set("sslmode", "require") } - q.Set("timezone", cfg.DB.Timezone) - // Construct url. dbUrl = url.URL{ - Scheme: cfg.DB.Driver, - User: url.UserPassword(cfg.DB.User, cfg.DB.Pass), - Host: cfg.DB.Host, - Path: cfg.DB.Database, + Scheme: dbInfo.Driver, + User: url.UserPassword(dbInfo.User, dbInfo.Pass), + Host: dbInfo.Host, + Path: dbInfo.Database, RawQuery: q.Encode(), } } @@ -118,10 +156,10 @@ func main() { // Register informs the sqlxtrace package of the driver that we will be using in our program. // It uses a default service name, in the below case "postgres.db". To use a custom service // name use RegisterWithServiceName. - sqltrace.Register(cfg.DB.Driver, &pq.Driver{}, sqltrace.WithServiceName(service)) - masterDb, err := sqlxtrace.Open(cfg.DB.Driver, dbUrl.String()) + sqltrace.Register(dbInfo.Driver, &pq.Driver{}, sqltrace.WithServiceName(service)) + masterDb, err := sqlxtrace.Open(dbInfo.Driver, dbUrl.String()) if err != nil { - log.Fatalf("main : Register DB : %s : %v", cfg.DB.Driver, err) + log.Fatalf("main : Register DB : %s : %v", dbInfo.Driver, err) } defer masterDb.Close() @@ -132,13 +170,15 @@ func main() { // process the request. v := webcontext.Values{ Now: time.Now(), - Env: cfg.Env, + Env: targetEnv, } ctx := context.WithValue(context.Background(), webcontext.KeyValues, &v) // Execute the migrations if err = schema.Migrate(ctx, masterDb, log, false); err != nil { - log.Fatalf("main : Migrate : %v", err) + return err } + log.Printf("main : Migrate : Completed") + return nil }