Skip to content

Commit

Permalink
S3 credentials overwrite (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
liweiyi88 authored Dec 12, 2022
1 parent 31d1503 commit 936fc28
Show file tree
Hide file tree
Showing 15 changed files with 599 additions and 646 deletions.
25 changes: 9 additions & 16 deletions cmd/apply.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
package cmd

import (
"fmt"
"log"
"os"
"sync"

"github.com/liweiyi88/onedump/dumpjob"
"github.com/liweiyi88/onedump/dump"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
)

var (
file string
)
var file string

var applyCmd = &cobra.Command{
Use: "apply -f /path/to/jobs.yaml",
Args: cobra.ExactArgs(0),
Short: "Dump db content from different sources to diferent destinations",
Short: "Dump database content from different sources to diferent destinations with a yaml config file.",
Run: func(cmd *cobra.Command, args []string) {
content, err := os.ReadFile(file)
if err != nil {
log.Fatalf("failed to read job file from %s, error: %v", file, err)
}

var oneDump dumpjob.OneDump
var oneDump dump.Dump
err = yaml.Unmarshal(content, &oneDump)
if err != nil {
log.Fatalf("failed to read job content from %s, error: %v", file, err)
Expand All @@ -42,23 +39,19 @@ var applyCmd = &cobra.Command{
return
}

resultCh := make(chan *dumpjob.JobResult)
resultCh := make(chan *dump.JobResult)

for _, job := range oneDump.Jobs {
go func(job dumpjob.Job, resultCh chan *dumpjob.JobResult) {
go func(job *dump.Job, resultCh chan *dump.JobResult) {
resultCh <- job.Run()
}(job, resultCh)
}

var wg sync.WaitGroup
wg.Add(numberOfJobs)
go func(resultCh chan *dumpjob.JobResult) {
go func(resultCh chan *dump.JobResult) {
for result := range resultCh {
if result.Error != nil {
fmt.Printf("Job %s failed, it took %s with error: %v \n", result.JobName, result.Elapsed, result.Error)
} else {
fmt.Printf("Job %s succeeded and it took %v \n", result.JobName, result.Elapsed)
}
result.Print()
wg.Done()
}
}(resultCh)
Expand All @@ -70,6 +63,6 @@ var applyCmd = &cobra.Command{

func init() {
rootCmd.AddCommand(applyCmd)
applyCmd.Flags().StringVarP(&file, "file", "f", "", "jobs yaml file path (required)")
applyCmd.Flags().StringVarP(&file, "file", "f", "", "jobs yaml file path.")
applyCmd.MarkFlagRequired("file")
}
45 changes: 0 additions & 45 deletions cmd/mysql.go

This file was deleted.

53 changes: 52 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,53 @@ package cmd

import (
"fmt"
"log"
"os"
"strings"

"github.com/liweiyi88/onedump/dump"
"github.com/spf13/cobra"
)

var (
sshHost, sshUser, sshPrivateKeyFile string
dbDsn string
jobName string
dumpOptions []string
gzip bool
)

var rootCmd = &cobra.Command{
Short: "onedump is a database dump, backup and load tool",
Use: "<dbdriver> </path/to/dump-file.sql>",
Short: "Dump database content from a source to a destination via cli command.",
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
driver := strings.TrimSpace(args[0])

dumpFile := strings.TrimSpace(args[1])
if dumpFile == "" {
log.Fatal("you must specify the dump file path. e.g. /download/dump.sql")
}

name := "dump via cli"
if strings.TrimSpace(jobName) != "" {
name = jobName
}

job := dump.NewJob(
name,
driver,
dumpFile,
dbDsn,
dump.WithGzip(gzip),
dump.WithSshHost(sshHost),
dump.WithSshUser(sshUser),
dump.WithPrivateKeyFile(sshPrivateKeyFile),
dump.WithDumpOptions(dumpOptions),
)

job.Run().Print()
},
}

func Execute() {
Expand All @@ -17,3 +57,14 @@ func Execute() {
os.Exit(1)
}
}

func init() {
rootCmd.Flags().StringVarP(&sshHost, "ssh-host", "", "", "SSH host e.g. yourdomain.com (you can omit port as it uses 22 by default) or 56.09.139.09:33. (required) ")
rootCmd.Flags().StringVarP(&sshUser, "ssh-user", "", "root", "SSH username")
rootCmd.Flags().StringVarP(&sshPrivateKeyFile, "privatekey", "f", "", "private key file path for SSH connection")
rootCmd.Flags().StringArrayVarP(&dumpOptions, "dump-options", "", nil, "use options to overwrite or add new dump command options. e.g. for mysql: --dump-options \"--no-create-info\" --dump-options \"--skip-comments\"")
rootCmd.Flags().StringVarP(&dbDsn, "db-dsn", "d", "", "the database dsn for connection. e.g. <dbUser>:<dbPass>@tcp(<dbHost>:<dbPort>)/<dbName>")
rootCmd.MarkFlagRequired("db-dsn")
rootCmd.Flags().BoolVarP(&gzip, "gzip", "g", true, "if need to gzip the file")
rootCmd.Flags().StringVarP(&jobName, "job-name", "", "", "The dump job name")
}
53 changes: 0 additions & 53 deletions cmd/ssh.go

This file was deleted.

7 changes: 6 additions & 1 deletion dump/dbconfig.go → driver/driver.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
package dump
package driver

type Driver interface {
GetDumpCommand() (string, []string, error)
GetSshDumpCommand() (string, error)
}

type DBConfig struct {
DBName string
Expand Down
53 changes: 23 additions & 30 deletions dump/mysqldumper.go → driver/mysql.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dump
package driver

import (
"fmt"
Expand All @@ -13,14 +13,14 @@ import (

const CredentialFilePrefix = "mysqldumpcred-"

type Mysql struct {
type MysqlDriver struct {
MysqlDumpBinaryPath string
Options []string
ViaSsh bool
*DBConfig
}

func NewMysqlDumper(dsn string, options []string, viaSsh bool) (*Mysql, error) {
func NewMysqlDriver(dsn string, options []string, viaSsh bool) (*MysqlDriver, error) {
config, err := mysql.ParseDSN(dsn)
if err != nil {
return nil, err
Expand All @@ -42,7 +42,7 @@ func NewMysqlDumper(dsn string, options []string, viaSsh bool) (*Mysql, error) {
commandOptions = options
}

return &Mysql{
return &MysqlDriver{
MysqlDumpBinaryPath: "mysqldump",
Options: commandOptions,
ViaSsh: viaSsh,
Expand All @@ -51,7 +51,7 @@ func NewMysqlDumper(dsn string, options []string, viaSsh bool) (*Mysql, error) {
}

// Get dump command used by ssh dumper.
func (mysql *Mysql) GetSshDumpCommand() (string, error) {
func (mysql *MysqlDriver) GetSshDumpCommand() (string, error) {
args, err := mysql.getDumpCommandArgs()
if err != nil {
return "", err
Expand All @@ -60,10 +60,26 @@ func (mysql *Mysql) GetSshDumpCommand() (string, error) {
return fmt.Sprintf("mysqldump %s", strings.Join(args, " ")), nil
}

func (mysql *MysqlDriver) GetDumpCommand() (string, []string, error) {
args, err := mysql.getDumpCommandArgs()

if err != nil {
return "", nil, fmt.Errorf("failed to get dump command args %w", err)
}

// check and get the binary path.
mysqldumpBinaryPath, err := exec.LookPath(mysql.MysqlDumpBinaryPath)
if err != nil {
return "", nil, fmt.Errorf("failed to find mysqldump executable %s %w", mysql.MysqlDumpBinaryPath, err)
}

return mysqldumpBinaryPath, args, nil
}

// Store the username password in a temp file, and use it with the mysqldump command.
// It avoids to expoes credentials when you run the mysqldump command as user can view the whole command via ps aux.
// Inspired by https://github.com/spatie/db-dumper
func (mysql *Mysql) getDumpCommandArgs() ([]string, error) {
func (mysql *MysqlDriver) getDumpCommandArgs() ([]string, error) {

args := []string{}

Expand All @@ -83,7 +99,7 @@ func (mysql *Mysql) getDumpCommandArgs() ([]string, error) {
return args, nil
}

func (mysql *Mysql) createCredentialFile() (string, error) {
func (mysql *MysqlDriver) createCredentialFile() (string, error) {
var fileName string

contents := `[client]
Expand All @@ -108,26 +124,3 @@ host = %s`

return file.Name(), nil
}

func (mysql *Mysql) Dump(dumpFile string, shouldGzip bool) error {
args, err := mysql.getDumpCommandArgs()

if err != nil {
return fmt.Errorf("failed to get dump command args %w", err)
}

// check and get the binary path.
mysqldumpBinaryPath, err := exec.LookPath(mysql.MysqlDumpBinaryPath)
if err != nil {
return fmt.Errorf("failed to find mysqldump executable %s %w", mysql.MysqlDumpBinaryPath, err)
}

cmd := exec.Command(mysqldumpBinaryPath, args...)

dump(cmd, dumpFile, shouldGzip, "")
if err != nil {
return err
}

return nil
}
Loading

0 comments on commit 936fc28

Please sign in to comment.