Skip to content

Commit

Permalink
[-] fix "Host in --pgurl parameter ignored if port is not specified"
Browse files Browse the repository at this point in the history
 [+] add --init command line argument
 Fixes #128, closes #129
  • Loading branch information
pashagolub committed May 14, 2020
1 parent 1f193e1 commit f441bfc
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 48 deletions.
45 changes: 22 additions & 23 deletions internal/cmdparser/cmdparser.go
Expand Up @@ -11,7 +11,7 @@ import (
flags "github.com/jessevdk/go-flags"
)

type cmdOptions struct {
type CmdOptions struct {
ClientName string `short:"c" long:"clientname" description:"Unique name for application instance" required:"True"`
Verbose bool `short:"v" long:"verbose" description:"Show verbose debug information" env:"PGTT_VERBOSE"`
Host string `short:"h" long:"host" description:"PG config DB host" default:"localhost" env:"PGTT_PGHOST"`
Expand All @@ -22,11 +22,12 @@ type cmdOptions struct {
Password string `long:"password" description:"PG config DB password" env:"PGTT_PGPASSWORD"`
SSLMode string `long:"sslmode" default:"disable" description:"What SSL priority use for connection" choice:"disable" choice:"require"`
PostgresURL DbURL `long:"pgurl" description:"PG config DB url" env:"PGTT_URL"`
Init bool `long:"init" description:"Initialize database schema to the latest version and exit. Can be used with --upgrade"`
Upgrade bool `long:"upgrade" description:"Upgrade database to the latest version"`
NoShellTasks bool `long:"no-shell-tasks" description:"Disable executing of shell tasks" env:"PGTT_NOSHELLTASKS"`
}

func (c cmdOptions) String() string {
func (c CmdOptions) String() string {
s := fmt.Sprintf("Client:%s Verbose:%t Host:%s:%s DB:%s User:%s ",
c.ClientName, c.Verbose, c.Host, c.Port, c.Dbname, c.User)
if c.PostgresURL.pgurl != nil {
Expand All @@ -49,22 +50,25 @@ func (d *DbURL) UnmarshalFlag(s string) error {
return err
}

//ParseCurl parses URL structure into cmdOptions
func (c *cmdOptions) ParseCurl(cmdURL *url.URL) error {
//ParseCurl parses URL structure into CmdOptions
func (c *CmdOptions) ParseCurl(cmdURL *url.URL) error {
var err error
if cmdURL == nil {
return nil
}
if !strings.HasPrefix(cmdURL.Scheme, "postgres") {
return fmt.Errorf("Incorrect URI scheme: %s. "+
"The URI scheme designator can be either postgresql:// or postgres://", cmdURL.Scheme)
}
var err error
c.Host, c.Port, err = net.SplitHostPort(cmdURL.Host)
// Restore default values
if err != nil {
c.Host = "localhost"
c.Port = "5432"
if strings.Contains(cmdURL.Host, ":") {
c.Host, c.Port, err = net.SplitHostPort(cmdURL.Host)
if err != nil {
return err
}
} else {
c.Host = cmdURL.Host
}

if cmdURL.User != nil {
c.User = cmdURL.User.Username()
c.Password, _ = cmdURL.User.Password()
Expand All @@ -86,40 +90,34 @@ func isPostgresURI(s string) bool {
}

// Parse will parse command line arguments and initialize pgengine
func Parse() error {
cmdOpts := new(cmdOptions)
func Parse() (*CmdOptions, error) {
cmdOpts := new(CmdOptions)
parser := flags.NewParser(cmdOpts, flags.PrintErrors)
var err error
if nonOptionArgs, err = parser.Parse(); err != nil {
if !flags.WroteHelp(err) {
parser.WriteHelp(os.Stdout)
return err
return nil, err
}
}
//--pgurl option
err = cmdOpts.ParseCurl(cmdOpts.PostgresURL.pgurl)
if err != nil {
pgengine.LogToDB("ERROR", err)
}
//non option arguments
if len(nonOptionArgs) > 0 && cmdOpts.PostgresURL.pgurl == nil {
cmdOpts.PostgresURL.pgurl, err = url.Parse(strings.Join(nonOptionArgs, ""))
if err != nil {
return err
return nil, err
}

}
//connection string in dbname
if isPostgresURI(cmdOpts.Dbname) && cmdOpts.PostgresURL.pgurl == nil {
cmdOpts.PostgresURL.pgurl, err = url.Parse(cmdOpts.Dbname)
if err != nil {
return err
return nil, err
}
}

err = cmdOpts.ParseCurl(cmdOpts.PostgresURL.pgurl)
if err != nil {
return err
return nil, err
}
pgengine.ClientName = cmdOpts.ClientName
pgengine.VerboseLogLevel = cmdOpts.Verbose
Expand All @@ -129,8 +127,9 @@ func Parse() error {
pgengine.User = cmdOpts.User
pgengine.Password = cmdOpts.Password
pgengine.SSLMode = cmdOpts.SSLMode
pgengine.InitOnly = cmdOpts.Init
pgengine.Upgrade = cmdOpts.Upgrade
pgengine.NoShellTasks = cmdOpts.NoShellTasks
pgengine.LogToDB("DEBUG", fmt.Sprintf("Starting new session... %s", cmdOpts))
return nil
return cmdOpts, nil
}
97 changes: 74 additions & 23 deletions internal/cmdparser/cmdparser_test.go
@@ -1,33 +1,84 @@
package cmdparser

import (
"net/url"
"os"
"testing"

"github.com/stretchr/testify/assert"
)

func TestMain(t *testing.T) {
os.Args = []string{0: "go-test"} //here 0th element stands for application name
assert.Error(t, Parse(), "Should fail without required parameter -c")
os.Args = []string{0: "go-test", "-c", "client01", "foo"}
assert.Error(t, Parse(), "Should fail for unknown parameter")
os.Args = []string{0: "go-test", "-c", "client01", "http://foo.bar/baz"}
assert.Error(t, Parse(), "Should fail for invalid URI scheme")
os.Args = []string{0: "go-test", "-c", "client01", "--pgurl=http://foo.bar/baz"}
assert.Error(t, Parse(), "Should fail for invalid URI scheme")
os.Args = []string{0: "go-test", "-c", "client01", "-d", "postgres:// "}
assert.Error(t, Parse(), "Should fail for invalid URI scheme")
os.Args = []string{0: "go-test", "-c", "client01", "postgres:// "}
assert.Error(t, Parse(), "Should fail for invalid URI scheme")
os.Args = []string{0: "go-test", "-c", "client01", "postgres://user:pwd@host/db"}
assert.NoError(t, Parse(), "Should not fail for correct URI scheme")
os.Args = []string{0: "go-test", "-c", "client01", "postgresql://user:pwd@host/db"}
assert.NoError(t, Parse(), "Should not fail for correct URI scheme")
os.Args = []string{0: "go-test", "-c", "client01", "-d", "postgres://user:pwd@host/db"}
assert.NoError(t, Parse(), "Should not fail for correct URI scheme in --dbname")
os.Args = []string{0: "go-test", "-c", "client01", "--pgurl=postgres://user:pwd@host/db"}
assert.NoError(t, Parse(), "Should not fail for correct URI scheme")
os.Args = []string{0: "go-test", "-c", "client01", "--pgurl=postgres://user:pwd@host/db?sslmode=require"}
assert.NoError(t, Parse(), "Should not fail for correct additional parameters")
func TestParseFail(t *testing.T) {
tests := [][]string{
{0: "go-test"},
{0: "go-test", "-c", "client01", "foo"},
{0: "go-test", "-c", "client01", "http://foo.bar/baz"},
{0: "go-test", "-c", "client01", "--pgurl=http://foo.bar/baz"},
{0: "go-test", "-c", "client01", "-d", "postgres:// "},
{0: "go-test", "-c", "client01", "postgres:// "},
{0: "go-test", "-c", "client01", "postgres://foo@bar:5432:5432/"},
}
for _, d := range tests {
os.Args = d
_, err := Parse()
assert.Error(t, err)
}
}

type data struct {
args []string
result CmdOptions
msg string
}

func DbURLFromString(s string) DbURL {
dburl := DbURL{}
dburl.pgurl, _ = url.Parse(s)
return dburl
}

func TestParseSuccessful(t *testing.T) {
tests := []data{
{
args: []string{0: "go-test", "-c", "client01", "-u", "user", "--password=pwd", "--host=host", "-d", "db"},
result: CmdOptions{ClientName: "client01", User: "user", Password: "pwd", Port: "5432", Host: "host", Dbname: "db"},
msg: "Stimple arguments without URL failed",
},
{
args: []string{0: "go-test", "-c", "client01", "postgres://user:pwd@host/db"},
result: CmdOptions{ClientName: "client01", User: "user", Password: "pwd", Port: "5432", Host: "host", Dbname: "db",
PostgresURL: DbURLFromString("postgres://user:pwd@host/db")},
msg: "Standalone URL without port failed",
},
{
args: []string{0: "go-test", "-c", "client01", "postgresql://user:pwd@host:5455/db"},
result: CmdOptions{ClientName: "client01", User: "user", Password: "pwd", Port: "5455", Host: "host", Dbname: "db",
PostgresURL: DbURLFromString("postgresql://user:pwd@host:5455/db")},
msg: "Standalone URL with port failed",
},
{
args: []string{0: "go-test", "-c", "client01", "-d", "postgres://user:pwd@host/db"},
result: CmdOptions{ClientName: "client01", User: "user", Password: "pwd", Port: "5432", Host: "host", Dbname: "db",
PostgresURL: DbURLFromString("postgres://user:pwd@host/db")},
msg: "URL specified in --dbname argument failed",
},
{
args: []string{0: "go-test", "-c", "client01", "--pgurl=postgres://user:pwd@host/db"},
result: CmdOptions{ClientName: "client01", User: "user", Password: "pwd", Port: "5432", Host: "host", Dbname: "db",
PostgresURL: DbURLFromString("postgres://user:pwd@host/db")},
msg: "URL in --pgurl without port failed",
},
{
args: []string{"go-test", "-c", "client01", "--pgurl=postgres://user:pwd@host/db?sslmode=require"},
result: CmdOptions{ClientName: "client01", User: "user", Password: "pwd", Port: "5432", Host: "host", Dbname: "db",
PostgresURL: DbURLFromString("postgres://user:pwd@host/db?sslmode=require")},
msg: "URL in --pgurl without port and with sslmode failed",
},
}
for _, d := range tests {
os.Args = d.args
c, err := Parse()
assert.NoError(t, err, d.msg)
assert.Equal(t, d.result.String(), c.String(), d.msg)
}
}
3 changes: 3 additions & 0 deletions internal/pgengine/bootstrap.go
Expand Up @@ -47,6 +47,9 @@ var SSLMode string = "disable"
// Upgrade parameter specifies if database should be upgraded to latest version
var Upgrade bool

// InitOnly parameter specifies if database schema should be initialized followed by exit
var InitOnly bool

// NoShellTasks parameter disables SHELL tasks executing
var NoShellTasks bool

Expand Down
7 changes: 5 additions & 2 deletions main.go
Expand Up @@ -20,14 +20,15 @@ import (

func main() {
ctx := context.Background()
if cmdparser.Parse() != nil {
if _, err := cmdparser.Parse(); err != nil {
os.Exit(2)
}
connctx, cancel := context.WithTimeout(ctx, 90*time.Second)
defer cancel()
if !pgengine.InitAndTestConfigDBConnection(connctx) {
os.Exit(2)
}
defer pgengine.FinalizeConfigDBConnection()
if pgengine.Upgrade {
if !pgengine.MigrateDb(ctx) {
os.Exit(3)
Expand All @@ -37,7 +38,9 @@ func main() {
os.Exit(3)
}
}
defer pgengine.FinalizeConfigDBConnection()
if pgengine.InitOnly {
os.Exit(0)
}
pgengine.SetupCloseHandler()
for scheduler.Run(ctx) == scheduler.ConnectionDroppped {
pgengine.ReconnectDbAndFixLeftovers(ctx)
Expand Down

0 comments on commit f441bfc

Please sign in to comment.