diff --git a/cmd/apply/apply.go b/cmd/apply/apply.go index 72ec9789..506020f8 100644 --- a/cmd/apply/apply.go +++ b/cmd/apply/apply.go @@ -24,6 +24,7 @@ var ( applyUser string applyPassword string applySchema string + applySSLMode string applyFile string applyPlan string applyAutoApprove bool @@ -50,6 +51,7 @@ func init() { ApplyCmd.Flags().StringVar(&applyUser, "user", "", "Database user name (required) (env: PGUSER)") ApplyCmd.Flags().StringVar(&applyPassword, "password", "", "Database password (optional, can also use PGPASSWORD env var)") ApplyCmd.Flags().StringVar(&applySchema, "schema", "public", "Schema name") + ApplyCmd.Flags().StringVar(&applySSLMode, "sslmode", "prefer", "SSL mode (disable, allow, prefer, require, verify-ca, verify-full) (env: PGSSLMODE)") // Desired state schema file flag ApplyCmd.Flags().StringVar(&applyFile, "file", "", "Path to desired state SQL schema file") @@ -82,6 +84,14 @@ func RunApply(cmd *cobra.Command, args []string) error { } } + // Derive final sslmode: use flag if provided, otherwise check environment variable + finalSSLMode := applySSLMode + if cmd == nil || !cmd.Flags().Changed("sslmode") { + if envSSLMode := os.Getenv("PGSSLMODE"); envSSLMode != "" { + finalSSLMode = envSSLMode + } + } + var migrationPlan *plan.Plan var err error @@ -117,6 +127,7 @@ func RunApply(cmd *cobra.Command, args []string) error { User: applyUser, Password: finalPassword, Schema: applySchema, + SSLMode: finalSSLMode, File: applyFile, ApplicationName: applyApplicationName, } @@ -130,7 +141,7 @@ func RunApply(cmd *cobra.Command, args []string) error { // Validate schema fingerprint if plan has one if migrationPlan.SourceFingerprint != nil { - err := validateSchemaFingerprint(migrationPlan, applyHost, applyPort, applyDB, applyUser, finalPassword, applySchema, applyApplicationName) + err := validateSchemaFingerprint(migrationPlan, applyHost, applyPort, applyDB, applyUser, finalPassword, applySchema, finalSSLMode, applyApplicationName) if err != nil { return err } @@ -171,7 +182,7 @@ func RunApply(cmd *cobra.Command, args []string) error { Database: applyDB, User: applyUser, Password: finalPassword, - SSLMode: "prefer", + SSLMode: finalSSLMode, ApplicationName: applyApplicationName, } @@ -225,9 +236,9 @@ func RunApply(cmd *cobra.Command, args []string) error { } // validateSchemaFingerprint validates that the current database schema matches the expected fingerprint -func validateSchemaFingerprint(migrationPlan *plan.Plan, host string, port int, db, user, password, schema, applicationName string) error { +func validateSchemaFingerprint(migrationPlan *plan.Plan, host string, port int, db, user, password, schema, sslmode, applicationName string) error { // Get current state from target database - currentStateIR, err := util.GetIRFromDatabase(host, port, db, user, password, schema, applicationName) + currentStateIR, err := util.GetIRFromDatabaseWithSSLMode(host, port, db, user, password, schema, sslmode, applicationName) if err != nil { return fmt.Errorf("failed to get current database state for fingerprint validation: %w", err) } diff --git a/cmd/dump/dump.go b/cmd/dump/dump.go index fb237525..a18d6f5e 100644 --- a/cmd/dump/dump.go +++ b/cmd/dump/dump.go @@ -19,6 +19,7 @@ var ( user string password string schema string + sslmode string multiFile bool file string ) @@ -40,6 +41,7 @@ func init() { DumpCmd.Flags().StringVar(&user, "user", "", "Database user name (required) (env: PGUSER)") DumpCmd.Flags().StringVar(&password, "password", "", "Database password (optional, can also use PGPASSWORD env var)") DumpCmd.Flags().StringVar(&schema, "schema", "public", "Schema name to dump (default: public)") + DumpCmd.Flags().StringVar(&sslmode, "sslmode", "prefer", "SSL mode (disable, allow, prefer, require, verify-ca, verify-full) (env: PGSSLMODE)") DumpCmd.Flags().BoolVar(&multiFile, "multi-file", false, "Output schema to multiple files organized by object type") DumpCmd.Flags().StringVar(&file, "file", "", "Output file path (required when --multi-file is used)") } @@ -60,6 +62,14 @@ func runDump(cmd *cobra.Command, args []string) error { } } + // Derive final sslmode: use flag if provided, otherwise check environment variable + finalSSLMode := sslmode + if cmd == nil || !cmd.Flags().Changed("sslmode") { + if envSSLMode := os.Getenv("PGSSLMODE"); envSSLMode != "" { + finalSSLMode = envSSLMode + } + } + // Build database connection config := &util.ConnectionConfig{ Host: host, @@ -67,7 +77,7 @@ func runDump(cmd *cobra.Command, args []string) error { Database: db, User: user, Password: finalPassword, - SSLMode: "prefer", + SSLMode: finalSSLMode, ApplicationName: "pgschema", } diff --git a/cmd/plan/plan.go b/cmd/plan/plan.go index abc86afb..ccae74b7 100644 --- a/cmd/plan/plan.go +++ b/cmd/plan/plan.go @@ -21,6 +21,7 @@ var ( planUser string planPassword string planSchema string + planSSLMode string planFile string outputHuman string outputJSON string @@ -46,6 +47,7 @@ func init() { PlanCmd.Flags().StringVar(&planUser, "user", "", "Database user name (required) (env: PGUSER)") PlanCmd.Flags().StringVar(&planPassword, "password", "", "Database password (optional, can also use PGPASSWORD env var)") PlanCmd.Flags().StringVar(&planSchema, "schema", "public", "Schema name") + PlanCmd.Flags().StringVar(&planSSLMode, "sslmode", "prefer", "SSL mode (disable, allow, prefer, require, verify-ca, verify-full) (env: PGSSLMODE)") // Desired state schema file flag PlanCmd.Flags().StringVar(&planFile, "file", "", "Path to desired state SQL schema file (required)") @@ -68,6 +70,14 @@ func runPlan(cmd *cobra.Command, args []string) error { } } + // Derive final sslmode: use flag if provided, otherwise check environment variable + finalSSLMode := planSSLMode + if cmd == nil || !cmd.Flags().Changed("sslmode") { + if envSSLMode := os.Getenv("PGSSLMODE"); envSSLMode != "" { + finalSSLMode = envSSLMode + } + } + // Create plan configuration config := &PlanConfig{ Host: planHost, @@ -76,6 +86,7 @@ func runPlan(cmd *cobra.Command, args []string) error { User: planUser, Password: finalPassword, Schema: planSchema, + SSLMode: finalSSLMode, File: planFile, ApplicationName: "pgschema", } @@ -110,6 +121,7 @@ type PlanConfig struct { User string Password string Schema string + SSLMode string File string ApplicationName string } @@ -130,7 +142,7 @@ func GeneratePlan(config *PlanConfig) (*plan.Plan, error) { } // Get current state from target database - currentStateIR, err := util.GetIRFromDatabaseWithIgnoreConfig(config.Host, config.Port, config.DB, config.User, config.Password, config.Schema, config.ApplicationName, ignoreConfig) + currentStateIR, err := util.GetIRFromDatabaseWithIgnoreConfigAndSSLMode(config.Host, config.Port, config.DB, config.User, config.Password, config.Schema, config.SSLMode, config.ApplicationName, ignoreConfig) if err != nil { return nil, fmt.Errorf("failed to get current state from database: %w", err) } @@ -249,6 +261,7 @@ func ResetFlags() { planUser = "" planPassword = "" planSchema = "public" + planSSLMode = "prefer" planFile = "" outputHuman = "" outputJSON = "" diff --git a/cmd/util/connection.go b/cmd/util/connection.go index ed8d6e83..270f5a69 100644 --- a/cmd/util/connection.go +++ b/cmd/util/connection.go @@ -78,7 +78,13 @@ func buildDSN(config *ConnectionConfig) string { } // GetIRFromDatabase connects to a database and extracts schema using the IR system +// Uses default SSL mode "prefer" func GetIRFromDatabase(host string, port int, db, user, password, schemaName, applicationName string) (*ir.IR, error) { + return GetIRFromDatabaseWithSSLMode(host, port, db, user, password, schemaName, "prefer", applicationName) +} + +// GetIRFromDatabaseWithSSLMode connects to a database and extracts schema using the IR system with custom SSL mode +func GetIRFromDatabaseWithSSLMode(host string, port int, db, user, password, schemaName, sslmode, applicationName string) (*ir.IR, error) { // Build database connection config := &ConnectionConfig{ Host: host, @@ -86,7 +92,7 @@ func GetIRFromDatabase(host string, port int, db, user, password, schemaName, ap Database: db, User: user, Password: password, - SSLMode: "prefer", + SSLMode: sslmode, ApplicationName: applicationName, } @@ -116,7 +122,13 @@ func GetIRFromDatabase(host string, port int, db, user, password, schemaName, ap } // GetIRFromDatabaseWithIgnoreConfig gets the IR from a database with ignore configuration +// Uses default SSL mode "prefer" func GetIRFromDatabaseWithIgnoreConfig(host string, port int, db, user, password, schemaName, applicationName string, ignoreConfig *ir.IgnoreConfig) (*ir.IR, error) { + return GetIRFromDatabaseWithIgnoreConfigAndSSLMode(host, port, db, user, password, schemaName, "prefer", applicationName, ignoreConfig) +} + +// GetIRFromDatabaseWithIgnoreConfigAndSSLMode gets the IR from a database with ignore configuration and custom SSL mode +func GetIRFromDatabaseWithIgnoreConfigAndSSLMode(host string, port int, db, user, password, schemaName, sslmode, applicationName string, ignoreConfig *ir.IgnoreConfig) (*ir.IR, error) { // Build database connection config := &ConnectionConfig{ Host: host, @@ -124,7 +136,7 @@ func GetIRFromDatabaseWithIgnoreConfig(host string, port int, db, user, password Database: db, User: user, Password: password, - SSLMode: "prefer", + SSLMode: sslmode, ApplicationName: applicationName, }