mirror of https://github.com/k3s-io/k3s
Add the ability to list etcd snapshots (#3303)
* add ability to list local and s3 etcd snapshotspull/3310/head
parent
02a5bee62f
commit
bcd8b67db4
|
@ -13,7 +13,7 @@ import (
|
|||
func main() {
|
||||
app := cmds.NewApp()
|
||||
app.Commands = []cli.Command{
|
||||
cmds.NewEtcdSnapshotCommand(etcdsnapshot.Run, cmds.NewEtcdSnapshotSubcommands(etcdsnapshot.Delete)),
|
||||
cmds.NewEtcdSnapshotCommand(etcdsnapshot.Run, cmds.NewEtcdSnapshotSubcommands(etcdsnapshot.Delete, etcdsnapshot.List)),
|
||||
}
|
||||
|
||||
if err := app.Run(configfilearg.MustParse(os.Args)); err != nil {
|
||||
|
|
|
@ -44,7 +44,7 @@ func main() {
|
|||
cmds.NewCRICTL(externalCLIAction("crictl", dataDir)),
|
||||
cmds.NewCtrCommand(externalCLIAction("ctr", dataDir)),
|
||||
cmds.NewCheckConfigCommand(externalCLIAction("check-config", dataDir)),
|
||||
cmds.NewEtcdSnapshotCommand(etcdsnapshotCommand, cmds.NewEtcdSnapshotSubcommands(etcdsnapshotCommand)),
|
||||
cmds.NewEtcdSnapshotCommand(etcdsnapshotCommand, cmds.NewEtcdSnapshotSubcommands(etcdsnapshotCommand, etcdsnapshotCommand)),
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
|
|
|
@ -43,7 +43,7 @@ func main() {
|
|||
cmds.NewKubectlCommand(kubectl.Run),
|
||||
cmds.NewCRICTL(crictl.Run),
|
||||
cmds.NewCtrCommand(ctr.Run),
|
||||
cmds.NewEtcdSnapshotCommand(etcdsnapshot.Run, cmds.NewEtcdSnapshotSubcommands(etcdsnapshot.Delete)),
|
||||
cmds.NewEtcdSnapshotCommand(etcdsnapshot.Run, cmds.NewEtcdSnapshotSubcommands(etcdsnapshot.Delete, etcdsnapshot.List)),
|
||||
}
|
||||
|
||||
err := app.Run(configfilearg.MustParse(os.Args))
|
||||
|
|
2
main.go
2
main.go
|
@ -27,7 +27,7 @@ func main() {
|
|||
cmds.NewAgentCommand(agent.Run),
|
||||
cmds.NewKubectlCommand(kubectl.Run),
|
||||
cmds.NewCRICTL(crictl.Run),
|
||||
cmds.NewEtcdSnapshotCommand(etcdsnapshot.Run, cmds.NewEtcdSnapshotSubcommands(etcdsnapshot.Delete)),
|
||||
cmds.NewEtcdSnapshotCommand(etcdsnapshot.Run, cmds.NewEtcdSnapshotSubcommands(etcdsnapshot.Delete, etcdsnapshot.List)),
|
||||
}
|
||||
|
||||
if err := app.Run(configfilearg.MustParse(os.Args)); err != nil {
|
||||
|
|
|
@ -95,7 +95,7 @@ func NewEtcdSnapshotCommand(action func(*cli.Context) error, subcommands []cli.C
|
|||
}
|
||||
}
|
||||
|
||||
func NewEtcdSnapshotSubcommands(delete func(ctx *cli.Context) error) []cli.Command {
|
||||
func NewEtcdSnapshotSubcommands(delete, list func(ctx *cli.Context) error) []cli.Command {
|
||||
return []cli.Command{
|
||||
{
|
||||
Name: "delete",
|
||||
|
@ -105,5 +105,14 @@ func NewEtcdSnapshotSubcommands(delete func(ctx *cli.Context) error) []cli.Comma
|
|||
Action: delete,
|
||||
Flags: EtcdSnapshotFlags,
|
||||
},
|
||||
{
|
||||
Name: "ls",
|
||||
Aliases: []string{"list", "l"},
|
||||
Usage: "List etcd snapshots",
|
||||
SkipFlagParsing: false,
|
||||
SkipArgReorder: true,
|
||||
Action: list,
|
||||
Flags: EtcdSnapshotFlags,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,11 @@ package etcdsnapshot
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/erikdubbelboer/gspt"
|
||||
"github.com/rancher/k3s/pkg/cli/cmds"
|
||||
|
@ -18,7 +21,7 @@ import (
|
|||
|
||||
// commandSetup setups up common things needed
|
||||
// for each etcd command.
|
||||
func commandSetup(app *cli.Context, cfg *cmds.Server) (string, error) {
|
||||
func commandSetup(app *cli.Context, cfg *cmds.Server, sc *server.Config) (string, error) {
|
||||
gspt.SetProcTitle(os.Args[0])
|
||||
|
||||
nodeName := app.String("node-name")
|
||||
|
@ -32,6 +35,21 @@ func commandSetup(app *cli.Context, cfg *cmds.Server) (string, error) {
|
|||
|
||||
os.Setenv("NODE_NAME", nodeName)
|
||||
|
||||
sc.DisableAgent = true
|
||||
sc.ControlConfig.DataDir = cfg.DataDir
|
||||
sc.ControlConfig.EtcdSnapshotName = cfg.EtcdSnapshotName
|
||||
sc.ControlConfig.EtcdSnapshotDir = cfg.EtcdSnapshotDir
|
||||
sc.ControlConfig.EtcdS3 = cfg.EtcdS3
|
||||
sc.ControlConfig.EtcdS3Endpoint = cfg.EtcdS3Endpoint
|
||||
sc.ControlConfig.EtcdS3EndpointCA = cfg.EtcdS3EndpointCA
|
||||
sc.ControlConfig.EtcdS3SkipSSLVerify = cfg.EtcdS3SkipSSLVerify
|
||||
sc.ControlConfig.EtcdS3AccessKey = cfg.EtcdS3AccessKey
|
||||
sc.ControlConfig.EtcdS3SecretKey = cfg.EtcdS3SecretKey
|
||||
sc.ControlConfig.EtcdS3BucketName = cfg.EtcdS3BucketName
|
||||
sc.ControlConfig.EtcdS3Region = cfg.EtcdS3Region
|
||||
sc.ControlConfig.EtcdS3Folder = cfg.EtcdS3Folder
|
||||
sc.ControlConfig.Runtime = &config.ControlRuntime{}
|
||||
|
||||
return server.ResolveDataDir(cfg.DataDir)
|
||||
}
|
||||
|
||||
|
@ -43,7 +61,9 @@ func Run(app *cli.Context) error {
|
|||
}
|
||||
|
||||
func run(app *cli.Context, cfg *cmds.Server) error {
|
||||
dataDir, err := commandSetup(app, cfg)
|
||||
var serverConfig server.Config
|
||||
|
||||
dataDir, err := commandSetup(app, cfg, &serverConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -52,22 +72,8 @@ func run(app *cli.Context, cfg *cmds.Server) error {
|
|||
return cmds.ErrCommandNoArgs
|
||||
}
|
||||
|
||||
var serverConfig server.Config
|
||||
serverConfig.DisableAgent = true
|
||||
serverConfig.ControlConfig.DataDir = dataDir
|
||||
serverConfig.ControlConfig.EtcdSnapshotName = cfg.EtcdSnapshotName
|
||||
serverConfig.ControlConfig.EtcdSnapshotDir = cfg.EtcdSnapshotDir
|
||||
serverConfig.ControlConfig.EtcdSnapshotRetention = 0 // disable retention check
|
||||
serverConfig.ControlConfig.EtcdS3 = cfg.EtcdS3
|
||||
serverConfig.ControlConfig.EtcdS3Endpoint = cfg.EtcdS3Endpoint
|
||||
serverConfig.ControlConfig.EtcdS3EndpointCA = cfg.EtcdS3EndpointCA
|
||||
serverConfig.ControlConfig.EtcdS3SkipSSLVerify = cfg.EtcdS3SkipSSLVerify
|
||||
serverConfig.ControlConfig.EtcdS3AccessKey = cfg.EtcdS3AccessKey
|
||||
serverConfig.ControlConfig.EtcdS3SecretKey = cfg.EtcdS3SecretKey
|
||||
serverConfig.ControlConfig.EtcdS3BucketName = cfg.EtcdS3BucketName
|
||||
serverConfig.ControlConfig.EtcdS3Region = cfg.EtcdS3Region
|
||||
serverConfig.ControlConfig.EtcdS3Folder = cfg.EtcdS3Folder
|
||||
serverConfig.ControlConfig.Runtime = &config.ControlRuntime{}
|
||||
serverConfig.ControlConfig.Runtime.ETCDServerCA = filepath.Join(dataDir, "tls", "etcd", "server-ca.crt")
|
||||
serverConfig.ControlConfig.Runtime.ClientETCDCert = filepath.Join(dataDir, "tls", "etcd", "client.crt")
|
||||
serverConfig.ControlConfig.Runtime.ClientETCDKey = filepath.Join(dataDir, "tls", "etcd", "client.key")
|
||||
|
@ -108,7 +114,9 @@ func Delete(app *cli.Context) error {
|
|||
}
|
||||
|
||||
func delete(app *cli.Context, cfg *cmds.Server) error {
|
||||
dataDir, err := commandSetup(app, cfg)
|
||||
var serverConfig server.Config
|
||||
|
||||
dataDir, err := commandSetup(app, cfg, &serverConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -118,21 +126,7 @@ func delete(app *cli.Context, cfg *cmds.Server) error {
|
|||
return errors.New("no snapshots given for removal")
|
||||
}
|
||||
|
||||
var serverConfig server.Config
|
||||
serverConfig.DisableAgent = true
|
||||
serverConfig.ControlConfig.DataDir = dataDir
|
||||
serverConfig.ControlConfig.EtcdSnapshotName = cfg.EtcdSnapshotName
|
||||
serverConfig.ControlConfig.EtcdSnapshotDir = cfg.EtcdSnapshotDir
|
||||
serverConfig.ControlConfig.EtcdS3 = cfg.EtcdS3
|
||||
serverConfig.ControlConfig.EtcdS3Endpoint = cfg.EtcdS3Endpoint
|
||||
serverConfig.ControlConfig.EtcdS3EndpointCA = cfg.EtcdS3EndpointCA
|
||||
serverConfig.ControlConfig.EtcdS3SkipSSLVerify = cfg.EtcdS3SkipSSLVerify
|
||||
serverConfig.ControlConfig.EtcdS3AccessKey = cfg.EtcdS3AccessKey
|
||||
serverConfig.ControlConfig.EtcdS3SecretKey = cfg.EtcdS3SecretKey
|
||||
serverConfig.ControlConfig.EtcdS3BucketName = cfg.EtcdS3BucketName
|
||||
serverConfig.ControlConfig.EtcdS3Region = cfg.EtcdS3Region
|
||||
serverConfig.ControlConfig.EtcdS3Folder = cfg.EtcdS3Folder
|
||||
serverConfig.ControlConfig.Runtime = &config.ControlRuntime{}
|
||||
serverConfig.ControlConfig.Runtime.KubeConfigAdmin = filepath.Join(dataDir, "cred", "admin.kubeconfig")
|
||||
|
||||
ctx := signals.SetupSignalHandler(context.Background())
|
||||
|
@ -147,3 +141,43 @@ func delete(app *cli.Context, cfg *cmds.Server) error {
|
|||
|
||||
return e.DeleteSnapshots(ctx, app.Args())
|
||||
}
|
||||
|
||||
func List(app *cli.Context) error {
|
||||
if err := cmds.InitLogging(); err != nil {
|
||||
return err
|
||||
}
|
||||
return list(app, &cmds.ServerConfig)
|
||||
}
|
||||
|
||||
func list(app *cli.Context, cfg *cmds.Server) error {
|
||||
var serverConfig server.Config
|
||||
|
||||
dataDir, err := commandSetup(app, cfg, &serverConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serverConfig.ControlConfig.DataDir = dataDir
|
||||
|
||||
ctx := signals.SetupSignalHandler(context.Background())
|
||||
e := etcd.NewETCD()
|
||||
e.SetControlConfig(&serverConfig.ControlConfig)
|
||||
|
||||
sf, err := e.ListSnapshots(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
|
||||
defer w.Flush()
|
||||
|
||||
for _, s := range sf {
|
||||
if cfg.EtcdS3 {
|
||||
fmt.Fprintf(w, "%s\t%d\t%s\n", s.Name, s.Size, s.CreatedAt.Format(time.RFC3339))
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\t%s\t%d\t%s\n", s.Name, s.Location, s.Size, s.CreatedAt.Format(time.RFC3339))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -871,9 +871,9 @@ type s3Config struct {
|
|||
Folder string `json:"folder,omitempty"`
|
||||
}
|
||||
|
||||
// snapshotFile represents a single snapshot and it's
|
||||
// SnapshotFile represents a single snapshot and it's
|
||||
// metadata.
|
||||
type snapshotFile struct {
|
||||
type SnapshotFile struct {
|
||||
Name string `json:"name"`
|
||||
// Location contains the full path of the snapshot. For
|
||||
// local paths, the location will be prefixed with "file://".
|
||||
|
@ -887,8 +887,8 @@ type snapshotFile struct {
|
|||
// listSnapshots provides a list of the currently stored
|
||||
// snapshots on disk or in S3 along with their relevant
|
||||
// metadata.
|
||||
func (e *ETCD) listSnapshots(ctx context.Context, snapshotDir string) ([]snapshotFile, error) {
|
||||
var snapshots []snapshotFile
|
||||
func (e *ETCD) listSnapshots(ctx context.Context, snapshotDir string) ([]SnapshotFile, error) {
|
||||
var snapshots []SnapshotFile
|
||||
|
||||
if e.config.EtcdS3 {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
@ -913,7 +913,7 @@ func (e *ETCD) listSnapshots(ctx context.Context, snapshotDir string) ([]snapsho
|
|||
return nil, err
|
||||
}
|
||||
|
||||
snapshots = append(snapshots, snapshotFile{
|
||||
snapshots = append(snapshots, SnapshotFile{
|
||||
Name: filepath.Base(obj.Key),
|
||||
NodeName: "s3",
|
||||
CreatedAt: &metav1.Time{
|
||||
|
@ -942,7 +942,7 @@ func (e *ETCD) listSnapshots(ctx context.Context, snapshotDir string) ([]snapsho
|
|||
nodeName := os.Getenv("NODE_NAME")
|
||||
|
||||
for _, f := range files {
|
||||
snapshots = append(snapshots, snapshotFile{
|
||||
snapshots = append(snapshots, SnapshotFile{
|
||||
Name: f.Name(),
|
||||
Location: "file://" + filepath.Join(snapshotDir, f.Name()),
|
||||
NodeName: nodeName,
|
||||
|
@ -970,6 +970,17 @@ func (e *ETCD) initS3IfNil(ctx context.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ListSnapshots is an exported wrapper method that wraps an
|
||||
// unexported method of the same name.
|
||||
func (e *ETCD) ListSnapshots(ctx context.Context) ([]SnapshotFile, error) {
|
||||
snapshotDir, err := snapshotDir(e.config)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get the snapshot dir")
|
||||
}
|
||||
|
||||
return e.listSnapshots(ctx, snapshotDir)
|
||||
}
|
||||
|
||||
// deleteSnapshots removes the given snapshots from
|
||||
// either local storage or S3.
|
||||
func (e *ETCD) DeleteSnapshots(ctx context.Context, snapshots []string) error {
|
||||
|
@ -1051,7 +1062,7 @@ func (e *ETCD) DeleteSnapshots(ctx context.Context, snapshots []string) error {
|
|||
}
|
||||
|
||||
// updateSnapshotData populates the given map with the contents of the given slice.
|
||||
func updateSnapshotData(data map[string]string, snapshotFiles []snapshotFile) error {
|
||||
func updateSnapshotData(data map[string]string, snapshotFiles []SnapshotFile) error {
|
||||
for _, v := range snapshotFiles {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
|
@ -1106,14 +1117,14 @@ func (e *ETCD) StoreSnapshotData(ctx context.Context) error {
|
|||
}
|
||||
|
||||
if snapshotConfigMap.Data == nil {
|
||||
snapshotConfigMap.Data = make(map[string]string, 0)
|
||||
snapshotConfigMap.Data = make(map[string]string)
|
||||
}
|
||||
|
||||
nodeName := os.Getenv("NODE_NAME")
|
||||
|
||||
// remove entries for this node only
|
||||
for k, v := range snapshotConfigMap.Data {
|
||||
var sf snapshotFile
|
||||
var sf SnapshotFile
|
||||
if err := json.Unmarshal([]byte(v), &sf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue