-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b7c6051
Showing
11 changed files
with
1,323 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.idea | ||
dist | ||
out | ||
kubedb | ||
vendor |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package cmd | ||
|
||
import ( | ||
"github.com/clevyr/kubedb/cmd/dump" | ||
"github.com/clevyr/kubedb/cmd/exec" | ||
"github.com/clevyr/kubedb/cmd/restore" | ||
"github.com/spf13/cobra" | ||
"k8s.io/client-go/util/homedir" | ||
"path/filepath" | ||
) | ||
|
||
var Command = &cobra.Command{ | ||
Use: "kubedb", | ||
Short: "Interact with a database inside of Kubernetes", | ||
PersistentPreRun: func(cmd *cobra.Command, args []string) { | ||
cmd.SilenceUsage = true | ||
}, | ||
} | ||
|
||
func Execute() error { | ||
return Command.Execute() | ||
} | ||
|
||
func init() { | ||
var kubeconfigDefault string | ||
if home := homedir.HomeDir(); home != "" { | ||
kubeconfigDefault = filepath.Join(home, ".kube", "config") | ||
} | ||
Command.PersistentFlags().String("kubeconfig", kubeconfigDefault, "absolute path to the kubeconfig file") | ||
Command.PersistentFlags().StringP("namespace", "n", "", "the namespace scope for this CLI request") | ||
|
||
Command.AddCommand( | ||
exec.Command, | ||
dump.Command, | ||
restore.Command, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
package dump | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"compress/gzip" | ||
"github.com/clevyr/kubedb/internal/kubernetes" | ||
"github.com/clevyr/kubedb/internal/postgres" | ||
"github.com/spf13/cobra" | ||
"io" | ||
_ "k8s.io/client-go/plugin/pkg/client/auth" | ||
"log" | ||
"os" | ||
"path" | ||
"strings" | ||
"text/template" | ||
"time" | ||
) | ||
|
||
var Command = &cobra.Command{ | ||
Use: "dump", | ||
Aliases: []string{"d"}, | ||
Short: "Dump a database", | ||
RunE: run, | ||
} | ||
|
||
var ( | ||
database string | ||
username string | ||
password string | ||
directory string | ||
gzipFile bool | ||
ifExists bool | ||
clean bool | ||
noOwner bool | ||
) | ||
|
||
func init() { | ||
Command.Flags().StringVarP(&database, "dbname", "d", "db", "Database name to connect to") | ||
Command.Flags().StringVarP(&username, "username", "U", "postgres", "Database username") | ||
Command.Flags().StringVarP(&password, "password", "p", "", "Database password") | ||
Command.Flags().StringVarP(&directory, "directory", "C", ".", "Directory to dump to") | ||
|
||
Command.Flags().BoolVar(&gzipFile, "gzip", false, "Gzip on disk") | ||
|
||
Command.Flags().BoolVar(&ifExists, "if-exists", true, "use IF EXISTS when dropping objects") | ||
Command.Flags().BoolVar(&clean, "clean", true, "clean (drop) database objects before recreating") | ||
Command.Flags().BoolVar(&noOwner, "no-owner", true, "skip restoration of object ownership in plain-text format") | ||
} | ||
|
||
func run(cmd *cobra.Command, args []string) (err error) { | ||
client, err := kubernetes.CreateClientForCmd(cmd) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
postgresPod, err := postgres.GetPod(client) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if password == "" { | ||
password, err = postgres.GetSecret(client) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
filename, err := generateFilename(directory, client.Namespace) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if _, err := os.Stat(path.Dir(filename)); os.IsNotExist(err) { | ||
err = os.Mkdir(path.Dir(filename), os.ModePerm) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
outfile, err := os.Create(filename) | ||
if err != nil { | ||
return err | ||
} | ||
defer outfile.Close() | ||
fileWriter := bufio.NewWriter(outfile) | ||
defer fileWriter.Flush() | ||
|
||
pr, pw := io.Pipe() | ||
|
||
log.Println("Dumping \"" + postgresPod.Name + "\" to \"" + filename + "\"") | ||
|
||
ch := make(chan error) | ||
go func() { | ||
err := kubernetes.Exec(client, postgresPod, buildCommand(), os.Stdin, pw, false) | ||
pw.Close() | ||
ch <- err | ||
}() | ||
|
||
if gzipFile { | ||
_, err = io.Copy(fileWriter, pr) | ||
} else { | ||
var gzr *gzip.Reader | ||
gzr, err = gzip.NewReader(pr) | ||
if err != nil { | ||
return err | ||
} | ||
defer gzr.Close() | ||
_, err = io.Copy(fileWriter, gzr) | ||
} | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = <-ch | ||
if err == nil { | ||
log.Println("Finished") | ||
} | ||
return err | ||
} | ||
|
||
func generateFilename(directory, namespace string) (string, error) { | ||
directory = path.Clean(directory) | ||
t, err := template.New("filename").Parse("{{.directory}}/{{.namespace}}-{{.now.Format \"2006-01-02-150405\"}}") | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
var tpl bytes.Buffer | ||
data := map[string]interface{}{ | ||
"directory": directory, | ||
"namespace": namespace, | ||
"now": time.Now(), | ||
} | ||
err = t.Execute(&tpl, data) | ||
|
||
tpl.WriteString(".sql") | ||
if gzipFile { | ||
tpl.WriteString(".gz") | ||
} | ||
|
||
return tpl.String(), err | ||
} | ||
|
||
func buildCommand() []string { | ||
cmd := []string{"PGPASSWORD=" + password, "pg_dump", "--username=" + username, database} | ||
if clean { | ||
cmd = append(cmd, "--clean") | ||
} | ||
if noOwner { | ||
cmd = append(cmd, "--no-owner") | ||
} | ||
if ifExists { | ||
cmd = append(cmd, "--if-exists") | ||
} | ||
cmd = append(cmd, "|", "gzip", "--force") | ||
return []string{"sh", "-c", strings.Join(cmd, " ")} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package exec | ||
|
||
import ( | ||
"github.com/clevyr/kubedb/internal/kubernetes" | ||
"github.com/clevyr/kubedb/internal/postgres" | ||
"github.com/docker/cli/cli/streams" | ||
"github.com/spf13/cobra" | ||
_ "k8s.io/client-go/plugin/pkg/client/auth" | ||
"log" | ||
"os" | ||
"strings" | ||
) | ||
|
||
var Command = &cobra.Command{ | ||
Use: "exec", | ||
Aliases: []string{"e", "shell"}, | ||
Short: "Connect to an interactive shell", | ||
RunE: run, | ||
} | ||
|
||
var ( | ||
database string | ||
username string | ||
password string | ||
) | ||
|
||
func init() { | ||
Command.Flags().StringVarP(&database, "dbname", "d", "db", "Database name to connect to") | ||
Command.Flags().StringVarP(&username, "username", "U", "postgres", "Database username") | ||
Command.Flags().StringVarP(&password, "password", "p", "", "Database password") | ||
} | ||
|
||
func run(cmd *cobra.Command, args []string) (err error) { | ||
client, err := kubernetes.CreateClientForCmd(cmd) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
postgresPod, err := postgres.GetPod(client) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if password == "" { | ||
password, err = postgres.GetSecret(client) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
log.Println("Execing into \"" + postgresPod.Name + "\"") | ||
|
||
stdin := streams.NewIn(os.Stdin) | ||
if err := stdin.SetRawTerminal(); err != nil{ | ||
return err | ||
} | ||
defer stdin.RestoreTerminal() | ||
|
||
return kubernetes.Exec(client, postgresPod, buildCommand(args), stdin, os.Stdout, stdin.IsTerminal()) | ||
} | ||
|
||
func buildCommand(args []string) []string { | ||
var cmd []string | ||
if len(args) == 0 { | ||
cmd = []string{"PGPASSWORD=" + password, "psql", "--username=" + username, database} | ||
} else { | ||
cmd = append([]string{"PGPASSWORD=" + password, "exec"}, args...) | ||
} | ||
return []string{"sh", "-c", strings.Join(cmd, " ")} | ||
} |
Oops, something went wrong.