Skip to content

Commit

Permalink
Add the ability to list etcd snapshots (k3s-io#3303)
Browse files Browse the repository at this point in the history
* add ability to list local and s3 etcd snapshots
  • Loading branch information
briandowns authored May 11, 2021
1 parent 02a5bee commit bcd8b67
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 45 deletions.
2 changes: 1 addition & 1 deletion cmd/etcdsnapshot/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion cmd/k3s/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
11 changes: 10 additions & 1 deletion pkg/cli/cmds/etcd_snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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 changes: 65 additions & 31 deletions pkg/cli/etcdsnapshot/etcd_snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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")
Expand All @@ -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)
}

Expand All @@ -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
}
Expand All @@ -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")
Expand Down Expand Up @@ -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
}
Expand All @@ -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())
Expand All @@ -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 changes: 20 additions & 9 deletions pkg/etcd/etcd.go
Original file line number Diff line number Diff line change
Expand Up @@ -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://".
Expand All @@ -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)
Expand All @@ -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{
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
Expand Down

0 comments on commit bcd8b67

Please sign in to comment.