Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI: Allow snapshot inspect to work on internal raft snapshots directly. #10089

Merged
merged 4 commits into from
Apr 23, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .changelog/10089.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
```release-note:improvement
cli: snapshot inspect command can now inspect raw snapshots from a server's data
dir. [[GH-10089](https://github.com/hashicorp/consul/pull/10089)]
banks marked this conversation as resolved.
Show resolved Hide resolved
```
46 changes: 36 additions & 10 deletions command/snapshot/inspect/snapshot_inspect.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package inspect

import (
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"sort"
"strings"

Expand Down Expand Up @@ -111,18 +114,41 @@ func (c *cmd) Run(args []string) int {
}
defer f.Close()

readFile, meta, err := snapshot.Read(hclog.New(nil), f)
if err != nil {
c.UI.Error(fmt.Sprintf("Error reading snapshot: %s", err))
}
defer func() {
if err := readFile.Close(); err != nil {
c.UI.Error(fmt.Sprintf("Failed to close temp snapshot: %v", err))
var readFile *os.File
var meta *raft.SnapshotMeta

if strings.ToLower(path.Base(file)) == "state.bin" {
// This is an internal raw raft snapshot not a gzipped archive one
// downloaded from the API, we can read it directly
readFile = f

// Assume the meta is colocated and error if not.
metaRaw, err := ioutil.ReadFile(path.Join(path.Dir(file), "meta.json"))
if err != nil {
c.UI.Error(fmt.Sprintf("Error reading meta.json from internal snapshot dir: %s", err))
return 1
}
if err := os.Remove(readFile.Name()); err != nil {
c.UI.Error(fmt.Sprintf("Failed to clean up temp snapshot: %v", err))
var metaDecoded raft.SnapshotMeta
err = json.Unmarshal(metaRaw, &metaDecoded)
if err != nil {
c.UI.Error(fmt.Sprintf("Error parsing meta.json from internal snapshot dir: %s", err))
return 1
}
}()
meta = &metaDecoded
} else {
readFile, meta, err = snapshot.Read(hclog.New(nil), f)
if err != nil {
c.UI.Error(fmt.Sprintf("Error reading snapshot: %s", err))
}
defer func() {
if err := readFile.Close(); err != nil {
c.UI.Error(fmt.Sprintf("Failed to close temp snapshot: %v", err))
}
if err := os.Remove(readFile.Name()); err != nil {
c.UI.Error(fmt.Sprintf("Failed to clean up temp snapshot: %v", err))
}
}()
}

info, err := c.enhance(readFile)
if err != nil {
Expand Down
20 changes: 20 additions & 0 deletions command/snapshot/inspect/snapshot_inspect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,23 @@ func TestSnapshotInspectKVDetailsDepthFilterCommand(t *testing.T) {
want := golden(t, t.Name(), ui.OutputWriter.String())
require.Equal(t, want, ui.OutputWriter.String())
}

// TestSnapshotInspectCommandRaw test reading a snaphost directly from a raft
// data dir.
func TestSnapshotInspectCommandRaw(t *testing.T) {

filepath := "./testdata/raw/state.bin"

// Inspect the snapshot
ui := cli.NewMockUi()
c := New(ui)
args := []string{filepath}

code := c.Run(args)
if code != 0 {
t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String())
}

want := golden(t, t.Name(), ui.OutputWriter.String())
require.Equal(t, want, ui.OutputWriter.String())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
ID 2-13-1602222343947
Size 5141
Index 13
Term 2
Version 1

Type Count Size
---- ---- ----
Register 3 1.7KB
ConnectCA 1 1.2KB
ConnectCAProviderState 1 1.1KB
Index 12 344B
Autopilot 1 199B
ConnectCAConfig 1 197B
FederationState 1 139B
SystemMetadata 1 68B
ChunkingState 1 12B
---- ---- ----
Total 5KB
1 change: 1 addition & 0 deletions command/snapshot/inspect/testdata/raw/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"Version":1,"ID":"2-13-1602222343947","Index":13,"Term":2,"Peers":"ka4xMjcuMC4wLjE6ODMwMA==","Configuration":{"Servers":[{"Suffrage":0,"ID":"a577b288-b354-770e-e909-da0972eb20e8","Address":"127.0.0.1:8300"}]},"ConfigurationIndex":1,"Size":5141}
Binary file added command/snapshot/inspect/testdata/raw/state.bin
Binary file not shown.
32 changes: 32 additions & 0 deletions website/content/commands/snapshot/inspect.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ snapshot of the state of the Consul servers which includes key/value entries,
service catalog, prepared queries, sessions, and ACLs. The snapshot is read
from the given file.

-> Typically this is used with Consul self-contained Snapshot files obtained
using the [`consul snapshot`](/commands/snapshot) command or [Snapshot
API](/api-docs/snapshot#generate-snapshot). If the file provided is named
`state.bin` however, the command will assume it is a raw raft snapshot in a
Consul server data directory and will attempt to read it directly. The
`state.bin` file must still be in the same directory as it's associated
`meta.json` file. This is useful for debugging data on live servers without
making a complete new snapshot via the CLI or API first.

The following fields are displayed when inspecting a snapshot:

- `ID` - A unique ID for the snapshot, only used for differentiation purposes.
Expand Down Expand Up @@ -106,6 +115,29 @@ $ consul snapshot inspect -kvdetails -kvdepth 3 -kvfilter vault/core backup.snap
Please see the [HTTP API](/api/snapshot) documentation for
more details about snapshot internals.

To inspect an internal snapshot directly from a Consul server data directory:

```shell-session
$ consul snapshot inspect /opt/consul/raft/snapshots/9-4600669-1618935304715/state.bin
ID 9-4600669-1618935304715
Size 4625420898
Index 4600669
Term 9
Version 1

Type Count Size
---- ---- ----
KVS 4089785 4.3GB
Register 9 5.2KB
CoordinateBatchUpdate 3 465B
Index 8 224B
Autopilot 1 199B
FederationState 1 139B
ChunkingState 1 12B
---- ---- ----
Total 4.3GB
```

#### Command Options

- `-kvdetails` - Optional, provides a space usage breakdown for any KV data stored in Consul.
Expand Down