Browse Source

Add the ability to list etcd snapshots (#3303)

* add ability to list local and s3 etcd snapshots
pull/3310/head
Brian Downs 4 years ago committed by GitHub
parent
commit
bcd8b67db4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      cmd/etcdsnapshot/main.go
  2. 2
      cmd/k3s/main.go
  3. 2
      cmd/server/main.go
  4. 2
      main.go
  5. 11
      pkg/cli/cmds/etcd_snapshot.go
  6. 96
      pkg/cli/etcdsnapshot/etcd_snapshot.go
  7. 29
      pkg/etcd/etcd.go

2
cmd/etcdsnapshot/main.go

@ -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 {

2
cmd/k3s/main.go

@ -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 {

2
cmd/server/main.go

@ -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

@ -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 {

11
pkg/cli/cmds/etcd_snapshot.go

@ -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,
},
}
}

96
pkg/cli/etcdsnapshot/etcd_snapshot.go

@ -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
}

29
pkg/etcd/etcd.go

@ -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…
Cancel
Save