diff --git a/.changelog/20824.txt b/.changelog/20824.txt new file mode 100644 index 0000000000..c067616dbe --- /dev/null +++ b/.changelog/20824.txt @@ -0,0 +1,3 @@ +```release-note:improvement +snapshot: Add `consul snapshot decode` CLI command to output a JSON object stream of all the snapshots data. +``` \ No newline at end of file diff --git a/command/registry.go b/command/registry.go index ca0baeeb31..381b86db9a 100644 --- a/command/registry.go +++ b/command/registry.go @@ -129,6 +129,7 @@ import ( exportedservices "github.com/hashicorp/consul/command/services/exportedservices" svcsregister "github.com/hashicorp/consul/command/services/register" "github.com/hashicorp/consul/command/snapshot" + snapdecode "github.com/hashicorp/consul/command/snapshot/decode" snapinspect "github.com/hashicorp/consul/command/snapshot/inspect" snaprestore "github.com/hashicorp/consul/command/snapshot/restore" snapsave "github.com/hashicorp/consul/command/snapshot/save" @@ -276,6 +277,7 @@ func RegisteredCommands(ui cli.Ui) map[string]mcli.CommandFactory { entry{"services export", func(ui cli.Ui) (cli.Command, error) { return svcsexport.New(ui), nil }}, entry{"services exported-services", func(ui cli.Ui) (cli.Command, error) { return exportedservices.New(ui), nil }}, entry{"snapshot", func(cli.Ui) (cli.Command, error) { return snapshot.New(), nil }}, + entry{"snapshot decode", func(ui cli.Ui) (cli.Command, error) { return snapdecode.New(ui), nil }}, entry{"snapshot inspect", func(ui cli.Ui) (cli.Command, error) { return snapinspect.New(ui), nil }}, entry{"snapshot restore", func(ui cli.Ui) (cli.Command, error) { return snaprestore.New(ui), nil }}, entry{"snapshot save", func(ui cli.Ui) (cli.Command, error) { return snapsave.New(ui), nil }}, diff --git a/command/snapshot/decode/snapshot_decode.go b/command/snapshot/decode/snapshot_decode.go new file mode 100644 index 0000000000..03151c093c --- /dev/null +++ b/command/snapshot/decode/snapshot_decode.go @@ -0,0 +1,218 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package decode + +import ( + "encoding/json" + "flag" + "fmt" + "io" + "os" + "path" + "strings" + + "github.com/hashicorp/consul-net-rpc/go-msgpack/codec" + "github.com/hashicorp/consul/agent/consul/fsm" + "github.com/hashicorp/consul/agent/consul/state" + "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/consul/command/flags" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/hashicorp/consul/proto/private/pbpeering" + "github.com/hashicorp/consul/snapshot" + "github.com/hashicorp/go-hclog" + "github.com/hashicorp/go-raftchunking" + "github.com/hashicorp/raft" + "github.com/mitchellh/cli" +) + +var requestTypeZeroValues = map[structs.MessageType]func() any{ + structs.RegisterRequestType: func() any { return new(structs.RegisterRequest) }, + structs.KVSRequestType: func() any { return new(structs.KVSRequest) }, + structs.SessionRequestType: func() any { return new(structs.SessionRequest) }, + structs.TombstoneRequestType: func() any { return new(structs.TombstoneRequest) }, + structs.CoordinateBatchUpdateType: func() any { return new(structs.Coordinates) }, + structs.PreparedQueryRequestType: func() any { return new(structs.PreparedQueryRequest) }, + structs.AutopilotRequestType: func() any { return new(structs.AutopilotSetConfigRequest) }, + structs.IntentionRequestType: func() any { return new(structs.IntentionRequest) }, + structs.ConnectCARequestType: func() any { return new(structs.CARequest) }, + structs.ConnectCAProviderStateType: func() any { return new(structs.CAConsulProviderState) }, + structs.ConnectCAConfigType: func() any { return new(structs.CAConfiguration) }, + structs.IndexRequestType: func() any { return new(state.IndexEntry) }, + structs.ACLTokenSetRequestType: func() any { return new(structs.ACLToken) }, + structs.ACLPolicySetRequestType: func() any { return new(structs.ACLPolicy) }, + structs.ConfigEntryRequestType: func() any { return new(structs.ConfigEntryRequest) }, + structs.ACLRoleSetRequestType: func() any { return new(structs.ACLRole) }, + structs.ACLBindingRuleSetRequestType: func() any { return new(structs.ACLBindingRule) }, + structs.ACLAuthMethodSetRequestType: func() any { return new(structs.ACLAuthMethod) }, + structs.ChunkingStateType: func() any { return new(raftchunking.State) }, + structs.FederationStateRequestType: func() any { return new(structs.FederationStateRequest) }, + structs.SystemMetadataRequestType: func() any { return new(structs.SystemMetadataEntry) }, + structs.ServiceVirtualIPRequestType: func() any { return new(state.ServiceVirtualIP) }, + structs.FreeVirtualIPRequestType: func() any { return new(state.FreeVirtualIP) }, + structs.PeeringWriteType: func() any { return new(pbpeering.Peering) }, + structs.PeeringTrustBundleWriteType: func() any { return new(pbpeering.PeeringTrustBundle) }, + structs.PeeringSecretsWriteType: func() any { return new(pbpeering.PeeringSecrets) }, + structs.ResourceOperationType: func() any { return new(pbresource.Resource) }, +} + +func New(ui cli.Ui) *cmd { + c := &cmd{UI: ui} + c.init() + return c +} + +type cmd struct { + UI cli.Ui + flags *flag.FlagSet + help string + format string + + encoder *json.Encoder +} + +func (c *cmd) Write(p []byte) (n int, err error) { + s := string(p) + c.UI.Output(strings.TrimRight(s, "\n")) + return len(s), nil +} + +func (c *cmd) init() { + c.flags = flag.NewFlagSet("", flag.ContinueOnError) + c.help = flags.Usage(help, c.flags) + c.encoder = json.NewEncoder(c) +} + +func (c *cmd) Run(args []string) int { + if err := c.flags.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + var file string + args = c.flags.Args() + + switch len(args) { + case 0: + c.UI.Error("Missing FILE argument") + return 1 + case 1: + file = args[0] + default: + c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, got %d)", len(args))) + return 1 + } + + // Open the file. + f, err := os.Open(file) + if err != nil { + c.UI.Error(fmt.Sprintf("Error opening snapshot file: %s", err)) + return 1 + } + defer f.Close() + + 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 := os.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 + } + 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)) + return 1 + } + 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)) + } + }() + } + + err = c.encoder.Encode(map[string]interface{}{ + "Type": "SnapshotHeader", + "Data": meta, + }) + if err != nil { + c.UI.Error(fmt.Sprintf("Error encoding snapshot header to the output stream: %v", err)) + return 1 + } + + err = c.decodeStream(readFile) + if err != nil { + c.UI.Error(fmt.Sprintf("Error extracting snapshot data: %s", err)) + return 1 + } + + return 0 +} + +// enhance utilizes ReadSnapshot to populate the struct with +// all of the snapshot's itemized data +func (c *cmd) decodeStream(file io.Reader) error { + handler := func(header *fsm.SnapshotHeader, msg structs.MessageType, dec *codec.Decoder) error { + name := structs.MessageType.String(msg) + var val interface{} + + if zeroVal, ok := requestTypeZeroValues[msg]; ok { + val = zeroVal() + } + + err := dec.Decode(&val) + if err != nil { + return fmt.Errorf("failed to decode msg type %v, error %v", name, err) + } + + err = c.encoder.Encode(map[string]interface{}{ + "Type": name, + "Data": val, + }) + + if err != nil { + return fmt.Errorf("failed to encode data into the object stream: %w", err) + } + + return nil + } + + return fsm.ReadSnapshot(file, handler) +} + +func (c *cmd) Synopsis() string { + return synopsis +} + +func (c *cmd) Help() string { + return c.help +} + +const synopsis = "Decodes the binary" +const help = ` +Usage: consul snapshot decode [options] FILE + + Decodes snapshot data and outputs a stream of line delimited JSON objects of the form: + + {"Type": "SnapshotHeader", "Data": {""}} + {"Type": "","Data": {}} + {"Type": "","Data": {}} + ... +` diff --git a/command/snapshot/decode/snapshot_decode_test.go b/command/snapshot/decode/snapshot_decode_test.go new file mode 100644 index 0000000000..bd456d558f --- /dev/null +++ b/command/snapshot/decode/snapshot_decode_test.go @@ -0,0 +1,107 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package decode + +import ( + "strings" + "testing" + + "github.com/hashicorp/consul/internal/testing/golden" + "github.com/hashicorp/consul/version/versiontest" + "github.com/mitchellh/cli" + "github.com/stretchr/testify/require" +) + +func TestSnapshotDecodeCommand_noTabs(t *testing.T) { + if strings.ContainsRune(New(cli.NewMockUi()).Help(), '\t') { + t.Fatal("help has tabs") + } +} + +func TestSnapshotDecodeCommand_Validation(t *testing.T) { + ui := cli.NewMockUi() + c := New(ui) + + cases := map[string]struct { + args []string + output string + }{ + "no file": { + []string{}, + "Missing FILE argument", + }, + "extra args": { + []string{"foo", "bar", "baz"}, + "Too many arguments", + }, + } + + for name, tc := range cases { + // Ensure our buffer is always clear + if ui.ErrorWriter != nil { + ui.ErrorWriter.Reset() + } + if ui.OutputWriter != nil { + ui.OutputWriter.Reset() + } + + code := c.Run(tc.args) + if code == 0 { + t.Errorf("%s: expected non-zero exit", name) + } + + output := ui.ErrorWriter.String() + if !strings.Contains(output, tc.output) { + t.Errorf("%s: expected %q to contain %q", name, output, tc.output) + } + } +} + +func TestSnapshotDecodeCommand(t *testing.T) { + cases := map[string]string{ + "no-kv": "./testdata/backup.snap", + "with-kv": "./testdata/backupWithKV.snap", + "all": "./testdata/all.snap", + } + + for name, fpath := range cases { + fpath := fpath + t.Run(name, func(t *testing.T) { + // Inspect the snapshot + ui := cli.NewMockUi() + c := New(ui) + args := []string{fpath} + + code := c.Run(args) + if code != 0 { + t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) + } + + actual := ui.OutputWriter.String() + fname := t.Name() + ".ce.golden" + if versiontest.IsEnterprise() { + fname = t.Name() + ".ent.golden" + } + want := golden.Get(t, actual, fname) + require.Equal(t, want, actual) + }) + } +} + +func TestSnapshotDecodeInvalidFile(t *testing.T) { + // Attempt to open a non-snapshot file. + filepath := "./testdata/TestSnapshotDecodeCommand/no-kv.golden" + + // Inspect the snapshot + ui := cli.NewMockUi() + c := New(ui) + args := []string{filepath} + + code := c.Run(args) + // Just check it was an error code returned and not a panic - originally this + // would panic. + if code == 0 { + t.Fatalf("should return an error code") + } +} diff --git a/command/snapshot/decode/testdata/TestSnapshotDecodeCommand/all.ce.golden b/command/snapshot/decode/testdata/TestSnapshotDecodeCommand/all.ce.golden new file mode 100644 index 0000000000..f6fc2969b1 --- /dev/null +++ b/command/snapshot/decode/testdata/TestSnapshotDecodeCommand/all.ce.golden @@ -0,0 +1,110 @@ +{"Data":{"Version":1,"ID":"2-62-1710350056693","Index":62,"Term":2,"Peers":"ka4xMjcuMC4wLjE6ODMwMA==","Configuration":{"Servers":[{"Suffrage":0,"ID":"065c1172-84a8-0bd1-269c-d24b2d0fb903","Address":"127.0.0.1:8300"}]},"ConfigurationIndex":1,"Size":25469},"Type":"SnapshotHeader"} +{"Data":"\n\u0007default\u0012\u0019Builtin Default Namespace0\n8\n","Type":"Unknown(65)"} +{"Data":"\n\u0007default\u0012\u0019Builtin Default Partition \t(\t","Type":"Unknown(69)"} +{"Data":{"Service":{"ServiceName":{"Name":"external"},"Peer":""},"IP":"0.0.0.3","ManualIPs":null,"CreateIndex":43,"ModifyIndex":43},"Type":"ServiceVirtualIP"} +{"Data":{"Service":{"ServiceName":{"Name":"foo"},"Peer":""},"IP":"0.0.0.2","ManualIPs":null,"CreateIndex":38,"ModifyIndex":38},"Type":"ServiceVirtualIP"} +{"Data":{"Service":{"ServiceName":{"Name":"web"},"Peer":""},"IP":"0.0.0.1","ManualIPs":null,"CreateIndex":37,"ModifyIndex":37},"Type":"ServiceVirtualIP"} +{"Data":{"IP":"0.0.0.3","IsCounter":true},"Type":"FreeVirtualIP"} +{"Data":{"Datacenter":"dc1","ID":"065c1172-84a8-0bd1-269c-d24b2d0fb903","Node":"mkeeler-C02F44FDMD6R","Address":"127.0.0.1","TaggedAddresses":{"lan":"127.0.0.1","lan_ipv4":"127.0.0.1","wan":"127.0.0.1","wan_ipv4":"127.0.0.1"},"NodeMeta":{"consul-network-segment":"","consul-version":"1.19.0"},"Service":null,"Check":null,"Checks":null,"Locality":null,"SkipNodeUpdate":false,"PeerName":"","Token":"","CreateIndex":21,"ModifyIndex":22},"Type":"Register"} +{"Data":{"Datacenter":"dc1","ID":"065c1172-84a8-0bd1-269c-d24b2d0fb903","Node":"mkeeler-C02F44FDMD6R","Address":"127.0.0.1","TaggedAddresses":{"lan":"127.0.0.1","lan_ipv4":"127.0.0.1","wan":"127.0.0.1","wan_ipv4":"127.0.0.1"},"NodeMeta":{"consul-network-segment":"","consul-version":"1.19.0"},"Service":{"ID":"consul","Service":"consul","Tags":null,"Address":"","Meta":{"grpc_port":"8502","grpc_tls_port":"8503","non_voter":"false","raft_version":"3","read_replica":"false","serf_protocol_current":"2","serf_protocol_max":"5","serf_protocol_min":"1","version":"1.19.0"},"Port":8300,"Weights":{"Passing":1,"Warning":1},"EnableTagOverride":false,"Proxy":{"Mode":"","MeshGateway":{},"Expose":{}},"Connect":{},"PeerName":"","CreateIndex":21,"ModifyIndex":21},"Check":null,"Checks":null,"Locality":null,"SkipNodeUpdate":false,"PeerName":"","Token":"","CreateIndex":21,"ModifyIndex":22},"Type":"Register"} +{"Data":{"Datacenter":"dc1","ID":"065c1172-84a8-0bd1-269c-d24b2d0fb903","Node":"mkeeler-C02F44FDMD6R","Address":"127.0.0.1","TaggedAddresses":{"lan":"127.0.0.1","lan_ipv4":"127.0.0.1","wan":"127.0.0.1","wan_ipv4":"127.0.0.1"},"NodeMeta":{"consul-network-segment":"","consul-version":"1.19.0"},"Service":null,"Check":{"Node":"mkeeler-C02F44FDMD6R","CheckID":"serfHealth","Name":"Serf Health Status","Status":"passing","Notes":"","Output":"Agent alive and reachable","ServiceID":"","ServiceName":"","ServiceTags":null,"Type":"","Interval":"","Timeout":"","ExposedPort":0,"Definition":{},"CreateIndex":21,"ModifyIndex":21},"Checks":null,"Locality":null,"SkipNodeUpdate":false,"PeerName":"","Token":"","CreateIndex":21,"ModifyIndex":22},"Type":"Register"} +{"Data":{"Datacenter":"","Op":"","Session":{"ID":"","Name":"","Node":"","LockDelay":0,"Behavior":"","TTL":"","NodeChecks":null,"ServiceChecks":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"Session"} +{"Data":{"AccessorID":"d56136c7-a3a8-ceac-da70-9bee9755d252","SecretID":"a4794332-5fe2-51e1-3a78-47c684bd181c","Description":"","NodeIdentities":[{"NodeName":"localhost","Datacenter":"dc1"}],"Local":false,"CreateTime":"2024-03-13T13:14:15.109823-04:00","Hash":"ibqA3nqRxdh2zAJSHY+nIphdDX/hpYOmgv3RASkkU1w=","CreateIndex":31,"ModifyIndex":31},"Type":"ACLToken"} +{"Data":{"AccessorID":"00000000-0000-0000-0000-000000000002","SecretID":"anonymous","Description":"Anonymous Token","Local":false,"CreateTime":"2024-03-13T13:14:09.953116-04:00","Hash":"tgCOyeidw+oaoZXQ9mHy6+EnY7atKoGaBzg2ndTwXl0=","CreateIndex":7,"ModifyIndex":7},"Type":"ACLToken"} +{"Data":{"AccessorID":"cbb26776-68df-d4ec-5b38-b40366ebf6c2","SecretID":"root","Description":"Initial Management Token","Policies":[{"ID":"00000000-0000-0000-0000-000000000001","Name":"global-management"}],"Local":false,"CreateTime":"2024-03-13T13:14:09.951135-04:00","Hash":"a5y709jeB8xI19PvumK2BeZG3u+1w3hfzyxf2s3UdDY=","CreateIndex":6,"ModifyIndex":6},"Type":"ACLToken"} +{"Data":{"ID":"00000000-0000-0000-0000-000000000001","Name":"global-management","Description":"A built-in policy that grants read and write access to all Consul features","Rules":"\nacl = \"write\"\nagent_prefix \"\" {\n\tpolicy = \"write\"\n}\nevent_prefix \"\" {\n\tpolicy = \"write\"\n}\nidentity_prefix \"\" {\n\tpolicy = \"write\"\n\tintentions = \"write\"\n}\nkey_prefix \"\" {\n\tpolicy = \"write\"\n}\nkeyring = \"write\"\nnode_prefix \"\" {\n\tpolicy = \"write\"\n}\noperator = \"write\"\nmesh = \"write\"\npeering = \"write\"\nquery_prefix \"\" {\n\tpolicy = \"write\"\n}\nservice_prefix \"\" {\n\tpolicy = \"write\"\n\tintentions = \"write\"\n}\nsession_prefix \"\" {\n\tpolicy = \"write\"\n}\npartition_prefix \"\" {\n\tmesh = \"write\"\n\tpeering = \"write\"\n\tnamespace \"default\" {\n\t\tnode_prefix \"\" {\n\t\t\tpolicy = \"write\"\n\t\t}\n\t\tagent_prefix \"\" {\n\t\t\tpolicy = \"write\"\n\t\t}\n\t}\n\tnamespace_prefix \"\" {\n\t\tacl = \"write\"\n\t\tidentity_prefix \"\" {\n\t\t\tpolicy = \"write\"\n\t\t\tintentions = \"write\"\n\t\t}\n\t\tkey_prefix \"\" {\n\t\t\tpolicy = \"write\"\n\t\t}\n\t\tnode_prefix \"\" {\n\t\t\t# node policy is restricted to read within a namespace\n\t\t\tpolicy = \"read\"\n\t\t}\n\t\tsession_prefix \"\" {\n\t\t\tpolicy = \"write\"\n\t\t}\n\t\tservice_prefix \"\" {\n\t\t\tpolicy = \"write\"\n\t\t\tintentions = \"write\"\n\t\t}\n\t}\n}\n","Hash":"NXp0drmQNPohGVdZ4mkRoOqe6Vtx2wYMuXHvQJNMbiE=","CreateIndex":4,"ModifyIndex":4},"Type":"ACLPolicy"} +{"Data":{"ID":"00000000-0000-0000-0000-000000000002","Name":"builtin/global-read-only","Description":"A built-in policy that grants read-only access to all Consul features","Rules":"\nacl = \"read\"\nagent_prefix \"\" {\n\tpolicy = \"read\"\n}\nevent_prefix \"\" {\n\tpolicy = \"read\"\n}\nidentity_prefix \"\" {\n\tpolicy = \"read\"\n\tintentions = \"read\"\n}\nkey_prefix \"\" {\n\tpolicy = \"read\"\n}\nkeyring = \"read\"\nnode_prefix \"\" {\n\tpolicy = \"read\"\n}\noperator = \"read\"\nmesh = \"read\"\npeering = \"read\"\nquery_prefix \"\" {\n\tpolicy = \"read\"\n}\nservice_prefix \"\" {\n\tpolicy = \"read\"\n\tintentions = \"read\"\n}\nsession_prefix \"\" {\n\tpolicy = \"read\"\n}\npartition_prefix \"\" {\n\tmesh = \"read\"\n\tpeering = \"read\"\n\tnamespace \"default\" {\n\t\tnode_prefix \"\" {\n\t\t\tpolicy = \"read\"\n\t\t}\n\t\tagent_prefix \"\" {\n\t\t\tpolicy = \"read\"\n\t\t}\n\t}\n\tnamespace_prefix \"\" {\n\t\tacl = \"read\"\n\t\tidentity_prefix \"\" {\n\t\t\tpolicy = \"read\"\n\t\t\tintentions = \"read\"\n\t\t}\n\t\tkey_prefix \"\" {\n\t\t\tpolicy = \"read\"\n\t\t}\n\t\tnode_prefix \"\" {\n\t\t\t# node policy is restricted to read within a namespace\n\t\t\tpolicy = \"read\"\n\t\t}\n\t\tsession_prefix \"\" {\n\t\t\tpolicy = \"read\"\n\t\t}\n\t\tservice_prefix \"\" {\n\t\t\tpolicy = \"read\"\n\t\t\tintentions = \"read\"\n\t\t}\n\t}\n}\n","Hash":"l1CO3kg8k1ikfGq3P309ZHfazlibtJ9RSGfDminehaE=","CreateIndex":5,"ModifyIndex":5},"Type":"ACLPolicy"} +{"Data":{"ID":"cefa650e-8538-974a-00a0-833c6aa4634a","Name":"test","Description":"","Rules":"node_prefix \"\" { policy = \"write\" }","Hash":"Utaz0IM7r4OLGrVW3ESeiZVGaaql+03cjTU7UHZiVJs=","CreateIndex":32,"ModifyIndex":32},"Type":"ACLPolicy"} +{"Data":{"ID":"edbab2c0-4bd2-052f-8a08-2f31e65930b2","Name":"test","Description":"","Policies":[{"ID":"cefa650e-8538-974a-00a0-833c6aa4634a","Name":"test"}],"Hash":"lfkCqRLtTKoFk50xI5Hxij13bfzsyg7IN7kYwnMP4Y0=","CreateIndex":33,"ModifyIndex":33},"Type":"ACLRole"} +{"Data":{"ID":"2a7708b7-913a-8c1f-593f-019c7192d549","Description":"","AuthMethod":"test","Selector":"","BindType":"service","BindName":"service","CreateIndex":35,"ModifyIndex":35},"Type":"ACLBindingRule"} +{"Data":{"Name":"test","Type":"jwt","Config":{"JWTValidationPubKeys":["-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENRw6ZwlBOx5XZKjcc1HhU00sDehc\n8nqeeSnRZLv89yT7M7qUOFDtR29FR/AFUSAEOFl1iIYLqNMElHs2VkgAZA==\n-----END PUBLIC KEY-----"]},"CreateIndex":34,"ModifyIndex":34},"Type":"ACLAuthMethod"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","ReapIndex":0,"Token":""},"Type":"Tombstone"} +{"Data":{"Datacenter":"","Op":"","Query":null,"Token":"root"},"Type":"PreparedQuery"} +{"Data":{"Datacenter":"","Config":{"CleanupDeadServers":false,"LastContactThreshold":0,"MaxTrailingLogs":0,"MinQuorum":0,"ServerStabilizationTime":0,"RedundancyZoneTag":"","DisableUpgradeMigration":false,"UpgradeVersionTag":"","CreateIndex":0,"ModifyIndex":0},"CAS":false,"Token":""},"Type":"Autopilot"} +{"Data":{"Op":"","Datacenter":"","Index":0,"Roots":null,"Config":null,"ProviderState":null,"Token":""},"Type":"ConnectCA"} +{"Data":{"ID":"fb:50:9b:45:1a:65:15:c1:68:57:73:5f:da:cd:b8:0d:0f:e2:26:eb:68:66:43:11:85:9d:67:a9:7a:56:9c:b9","PrivateKey":"-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMnCtDuifZocILamO9VdbQtXoY6SGSDVknyV+HwbRRzUoAoGCCqGSM49\nAwEHoUQDQgAEf0yW1ejMmToqCbhtSPvlXylSnS8bxJtJD6fs4VGYh7567DjGfBda\nEdgefebLuDdPPl9xAzNOihV29jSb6WheUQ==\n-----END EC PRIVATE KEY-----\n","RootCert":"-----BEGIN CERTIFICATE-----\nMIICDjCCAbOgAwIBAgIBDjAKBggqhkjOPQQDAjAwMS4wLAYDVQQDEyVwcmktZ2Fn\nMXhrYS5jb25zdWwuY2EuZmM2YmY5NDYuY29uc3VsMB4XDTI0MDMxMzE3MTQwOVoX\nDTM0MDMxMTE3MTQwOVowMDEuMCwGA1UEAxMlcHJpLWdhZzF4a2EuY29uc3VsLmNh\nLmZjNmJmOTQ2LmNvbnN1bDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABH9MltXo\nzJk6Kgm4bUj75V8pUp0vG8SbSQ+n7OFRmIe+euw4xnwXWhHYHn3my7g3Tz5fcQMz\nTooVdvY0m+loXlGjgb0wgbowDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB\nAf8wKQYDVR0OBCIEILHtYgr0Zfai/xwJ6PEXOiNAtlDhn3ZSRkZcQNCGq+RnMCsG\nA1UdIwQkMCKAILHtYgr0Zfai/xwJ6PEXOiNAtlDhn3ZSRkZcQNCGq+RnMD8GA1Ud\nEQQ4MDaGNHNwaWZmZTovL2ZjNmJmOTQ2LTk3MzgtMzNmMi1hYmI2LThlYWI2ZGU1\nNmZiYS5jb25zdWwwCgYIKoZIzj0EAwIDSQAwRgIhAL/pd0eKuR/DoWBCYBV2ATNO\nPXjoamqziYct83HMqfv+AiEAzys8TxOmt6h83TujE+2CFmZxRZ1wTdUHPC4efjOb\nahw=\n-----END CERTIFICATE-----\n","IntermediateCert":"","CreateIndex":13,"ModifyIndex":15},"Type":"ConnectCAProviderState"} +{"Data":{"Provider":"consul","Config":{"IntermediateCertTTL":"8760h","LeafCertTTL":"72h","RootCertTTL":"87600h"},"State":null,"ForceWithoutCrossSigning":false,"CreateIndex":12,"ModifyIndex":16},"Type":"ConnectCAConfig"} +{"Data":{"Op":"","Datacenter":"","Entry":{"Kind":"api-gateway","Name":"apigw","Listeners":[{"Name":"tcp","Hostname":"","Port":443,"Protocol":"tcp","TLS":{"Certificates":[{"Kind":"inline-certificate","Name":"blah","SectionName":""}],"MaxVersion":"","MinVersion":"","CipherSuites":null}},{"Name":"http","Hostname":"","Port":8080,"Protocol":"http","TLS":{"Certificates":null,"MaxVersion":"","MinVersion":"","CipherSuites":null}}],"Status":{"Conditions":[{"Type":"Accepted","Status":"True","Reason":"Accepted","Message":"gateway is valid","Resource":{"Kind":"","Name":"","SectionName":""},"LastTransitionTime":"2024-03-13T17:14:16.170758Z"},{"Type":"Conflicted","Status":"False","Reason":"NoConflict","Message":"listener has no route conflicts","Resource":{"Kind":"api-gateway","Name":"apigw","SectionName":"http"},"LastTransitionTime":"2024-03-13T17:14:16.170763Z"},{"Type":"Conflicted","Status":"False","Reason":"NoConflict","Message":"listener has no route conflicts","Resource":{"Kind":"api-gateway","Name":"apigw","SectionName":"tcp"},"LastTransitionTime":"2024-03-13T17:14:16.170763Z"},{"Type":"ResolvedRefs","Status":"True","Reason":"ResolvedRefs","Message":"resolved refs","Resource":{"Kind":"api-gateway","Name":"apigw","SectionName":"http"},"LastTransitionTime":"2024-03-13T17:14:16.170756Z"},{"Type":"ResolvedRefs","Status":"True","Reason":"ResolvedRefs","Message":"resolved refs","Resource":{"Kind":"api-gateway","Name":"apigw","SectionName":"tcp"},"LastTransitionTime":"2024-03-13T17:14:16.170757Z"}]},"Hash":6495004227697965289,"CreateIndex":48,"ModifyIndex":49},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Op":"","Datacenter":"","Entry":{"Kind":"bound-api-gateway","Name":"apigw","Listeners":[{"Name":"tcp","Routes":[{"Kind":"tcp-route","Name":"fake","SectionName":""}],"Certificates":[{"Kind":"inline-certificate","Name":"blah","SectionName":""}]},{"Name":"http","Routes":null,"Certificates":null}],"Services":{"fake":[{"Kind":"tcp-route","Name":"fake","SectionName":""}]},"CreateIndex":50,"ModifyIndex":55},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Op":"","Datacenter":"","Entry":{"Kind":"exported-services","Name":"default","Services":[{"Name":"web","Namespace":"default","Consumers":[{"Peer":"other"}]}],"Hash":15272881108129132528,"CreateIndex":46,"ModifyIndex":46},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Op":"","Datacenter":"","Entry":{"Kind":"http-route","Name":"web","Parents":[{"Kind":"api-gateway","Name":"apigw","SectionName":"http"}],"Rules":[{"Filters":{"Headers":null,"URLRewrite":null,"RetryFilter":null,"TimeoutFilter":null,"JWT":null},"ResponseFilters":{"Headers":null},"Matches":null,"Services":[{"Name":"fake","Weight":1,"Filters":{"Headers":null,"URLRewrite":null,"RetryFilter":null,"TimeoutFilter":null,"JWT":null},"ResponseFilters":{"Headers":null}}]}],"Hostnames":null,"Status":{"Conditions":[{"Type":"Accepted","Status":"False","Reason":"InvalidDiscoveryChain","Message":"route protocol does not match targeted service protocol","Resource":{"Kind":"","Name":"","SectionName":""},"LastTransitionTime":"2024-03-13T17:14:16.417825Z"},{"Type":"Bound","Status":"False","Reason":"FailedToBind","Message":"failed to bind route to gateway apigw: route has not been accepted","Resource":{"Kind":"api-gateway","Name":"apigw","SectionName":"http"},"LastTransitionTime":"2024-03-13T17:14:16.417832Z"}]},"Hash":1173445267586960895,"CreateIndex":56,"ModifyIndex":57},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Op":"","Datacenter":"","Entry":{"Kind":"ingress-gateway","Name":"api","TLS":{"Enabled":false},"Listeners":null,"Hash":15966540600821199974,"CreateIndex":42,"ModifyIndex":42},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Op":"","Datacenter":"","Entry":{"Kind":"inline-certificate","Name":"blah","Certificate":"-----BEGIN CERTIFICATE-----\nMIICnjCCAkSgAwIBAgIQAxVHhSG0wSbdZm+3ToYAkDAKBggqhkjOPQQDAjCBuTEL\nMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2Nv\nMRowGAYDVQQJExExMDEgU2Vjb25kIFN0cmVldDEOMAwGA1UEERMFOTQxMDUxFzAV\nBgNVBAoTDkhhc2hpQ29ycCBJbmMuMUAwPgYDVQQDEzdDb25zdWwgQWdlbnQgQ0Eg\nMjgwNzE4MDMxODA1Mjk2OTA1NzQ4MzU3NjI1MTI5ODQ5NDA5NjI3MCAXDTIzMTEw\nMjE1Mjk0NVoYDzIxMjMxMDA5MTUyOTQ1WjAcMRowGAYDVQQDExFjbGllbnQuZGMx\nLmNvbnN1bDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKvl1yhbsI9r7IxJxLrt\nZTNYXkCXuFy8q3gsokMqsl/MUynrIBrd9NrZEQA91ZArUYzF1+QlxM6D4hRJc5CR\n3x6jgccwgcQwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr\nBgEFBQcDATAMBgNVHRMBAf8EAjAAMCkGA1UdDgQiBCCvXve+zMFSJMXNS3l3YL9k\n2QH8zF74wa+TlwFSaQEjGzArBgNVHSMEJDAigCBGa65jF6Wwq9OmdbgJIRCYv++x\nHG8dRBUpwvSk0Mk1+jAtBgNVHREEJjAkghFjbGllbnQuZGMxLmNvbnN1bIIJbG9j\nYWxob3N0hwR/AAABMAoGCCqGSM49BAMCA0gAMEUCIBLqa1Zh3KUE0RiQzWdoYXkU\nwZo5aBw9ujqzLyAqxToFAiEAihWmc4r6lDYRR35X4QB1nTT92POJRClsfLPOTRG5\nrsU=\n-----END CERTIFICATE-----","PrivateKey":"-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEINE2CQhnu7ipo67FGbEBRXoYRCTM4uJdHgNRTrdkAnHCoAoGCCqGSM49\nAwEHoUQDQgAEq+XXKFuwj2vsjEnEuu1lM1heQJe4XLyreCyiQyqyX8xTKesgGt30\n2tkRAD3VkCtRjMXX5CXEzoPiFElzkJHfHg==\n-----END EC PRIVATE KEY-----","Hash":3420847368935414704,"CreateIndex":47,"ModifyIndex":47},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Op":"","Datacenter":"","Entry":{"Kind":"jwt-provider","Name":"whocare","JSONWebKeySet":{"Local":{"Filename":"/tmp/jwks.json"}},"ClockSkewSeconds":30,"Hash":11700458008192259302,"CreateIndex":58,"ModifyIndex":58},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Op":"","Datacenter":"","Entry":{"Kind":"mesh","TransparentProxy":{"MeshDestinationsOnly":false},"AllowEnablingPermissiveMutualTLS":true,"Hash":10541910966466195416,"CreateIndex":45,"ModifyIndex":45},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Op":"","Datacenter":"","Entry":{"Kind":"proxy-defaults","Name":"global","Config":null,"TransparentProxy":{},"MeshGateway":{},"Expose":{},"AccessLogs":{},"Hash":2982631359577466273,"CreateIndex":36,"ModifyIndex":36},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Op":"","Datacenter":"","Entry":{"Kind":"sameness-group","Name":"default","DefaultForFailover":true,"IncludeLocal":true,"Members":[{"Peer":"other"}],"Hash":8697701191886031964,"CreateIndex":51,"ModifyIndex":51},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Op":"","Datacenter":"","Entry":{"Kind":"service-defaults","Name":"foo","Protocol":"http","TransparentProxy":{},"MeshGateway":{},"Expose":{},"Hash":6851507846005072818,"CreateIndex":38,"ModifyIndex":38},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Op":"","Datacenter":"","Entry":{"Kind":"service-defaults","Name":"web","Protocol":"http","TransparentProxy":{},"MeshGateway":{},"Expose":{},"Hash":7641900064431671148,"CreateIndex":37,"ModifyIndex":37},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Op":"","Datacenter":"","Entry":{"Kind":"service-intentions","Name":"web","Sources":[{"Name":"api","Action":"allow","Precedence":9,"Type":"consul"}],"Hash":1271129721167983805,"CreateIndex":44,"ModifyIndex":44},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Op":"","Datacenter":"","Entry":{"RequestTimeout":"3s","Kind":"service-resolver","Name":"web","Hash":3307964110661939386,"CreateIndex":41,"ModifyIndex":41},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Op":"","Datacenter":"","Entry":{"Kind":"service-router","Name":"foo","Routes":[{"Match":{"HTTP":{"PathPrefix":"/foo"}},"Destination":{"Service":"web","Namespace":"default","Partition":"default"}}],"Hash":101247989479106989,"CreateIndex":39,"ModifyIndex":39},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Op":"","Datacenter":"","Entry":{"Kind":"service-splitter","Name":"web","Splits":[{"Weight":100,"Service":"web"}],"Hash":18208480688038965425,"CreateIndex":40,"ModifyIndex":40},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Op":"","Datacenter":"","Entry":{"Kind":"tcp-route","Name":"fake","Parents":[{"Kind":"api-gateway","Name":"apigw","SectionName":"tcp"}],"Services":[{"Name":"fake"}],"Status":{"Conditions":[{"Type":"Accepted","Status":"True","Reason":"Accepted","Message":"route is valid","Resource":{"Kind":"","Name":"","SectionName":""},"LastTransitionTime":"2024-03-13T17:14:16.353589Z"},{"Type":"Bound","Status":"True","Reason":"Bound","Message":"successfully bound route","Resource":{"Kind":"api-gateway","Name":"apigw","SectionName":"tcp"},"LastTransitionTime":"2024-03-13T17:14:16.353602Z"}]},"Hash":4006422401340817930,"CreateIndex":52,"ModifyIndex":54},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Op":"","Datacenter":"","Entry":{"Kind":"terminating-gateway","Name":"external","Services":[{"Name":"external"}],"Hash":15903386135938294095,"CreateIndex":43,"ModifyIndex":43},"Token":""},"Type":"ConfigEntry"} +{"Data":{"Datacenter":"","Op":"upsert","State":{"Datacenter":"dc1","UpdatedAt":"2024-03-13T17:14:10.392908Z","PrimaryModifyIndex":23,"CreateIndex":23,"ModifyIndex":23},"Token":""},"Type":"FederationState"} +{"Data":{"Key":"intention-format","Value":"config-entry","CreateIndex":17,"ModifyIndex":17},"Type":"SystemMetadata"} +{"Data":{"Key":"reporting-process-id","Value":"01HRWCX8N4S6GEPX7EMTK17B6F","CreateIndex":20,"ModifyIndex":20},"Type":"SystemMetadata"} +{"Data":{"Key":"server-management-token","Value":"16ccea12-b43d-eb04-d1f6-205124572d70","CreateIndex":8,"ModifyIndex":8},"Type":"SystemMetadata"} +{"Data":{"Key":"virtual-ips","Value":"true","CreateIndex":18,"ModifyIndex":18},"Type":"SystemMetadata"} +{"Data":{"Key":"virtual-ips-term-gateway","Value":"true","CreateIndex":19,"ModifyIndex":19},"Type":"SystemMetadata"} +{"Data":{"Key":"acl-auth-methods","Value":34},"Type":"Index"} +{"Data":{"Key":"acl-binding-rules","Value":35},"Type":"Index"} +{"Data":{"Key":"acl-policies","Value":32},"Type":"Index"} +{"Data":{"Key":"acl-roles","Value":33},"Type":"Index"} +{"Data":{"Key":"acl-token-bootstrap","Value":6},"Type":"Index"} +{"Data":{"Key":"acl-tokens","Value":31},"Type":"Index"} +{"Data":{"Key":"checks","Value":21},"Type":"Index"} +{"Data":{"Key":"config-entries","Value":58},"Type":"Index"} +{"Data":{"Key":"connect-ca-builtin","Value":15},"Type":"Index"} +{"Data":{"Key":"connect-ca-builtin-serial","Value":15},"Type":"Index"} +{"Data":{"Key":"connect-ca-roots","Value":16},"Type":"Index"} +{"Data":{"Key":"connect-intentions","Value":17},"Type":"Index"} +{"Data":{"Key":"federation-states","Value":23},"Type":"Index"} +{"Data":{"Key":"gateway-services","Value":43},"Type":"Index"} +{"Data":{"Key":"kind_service_names.partition.default:namespace.default:typical","Value":21},"Type":"Index"} +{"Data":{"Key":"kind_service_names.partition.default:typical","Value":21},"Type":"Index"} +{"Data":{"Key":"kind_service_names.typical","Value":21},"Type":"Index"} +{"Data":{"Key":"mesh-topology","Value":42},"Type":"Index"} +{"Data":{"Key":"nodes","Value":22},"Type":"Index"} +{"Data":{"Key":"partition","Value":9},"Type":"Index"} +{"Data":{"Key":"partition.default:acl-auth-methods","Value":34},"Type":"Index"} +{"Data":{"Key":"partition.default:acl-binding-rules","Value":35},"Type":"Index"} +{"Data":{"Key":"partition.default:acl-policies","Value":32},"Type":"Index"} +{"Data":{"Key":"partition.default:acl-roles","Value":33},"Type":"Index"} +{"Data":{"Key":"partition.default:acl-tokens","Value":31},"Type":"Index"} +{"Data":{"Key":"partition.default:kvs","Value":28},"Type":"Index"} +{"Data":{"Key":"partition.default:namespace","Value":10},"Type":"Index"} +{"Data":{"Key":"partition.default:namespace.default:acl-auth-methods","Value":34},"Type":"Index"} +{"Data":{"Key":"partition.default:namespace.default:acl-binding-rules","Value":35},"Type":"Index"} +{"Data":{"Key":"partition.default:namespace.default:acl-policies","Value":32},"Type":"Index"} +{"Data":{"Key":"partition.default:namespace.default:acl-roles","Value":33},"Type":"Index"} +{"Data":{"Key":"partition.default:namespace.default:acl-tokens","Value":31},"Type":"Index"} +{"Data":{"Key":"partition.default:namespace.default:kvs","Value":28},"Type":"Index"} +{"Data":{"Key":"partition.default:namespace.default:sessions","Value":29},"Type":"Index"} +{"Data":{"Key":"partition.default:namespace.default:tombstones","Value":28},"Type":"Index"} +{"Data":{"Key":"partition.default:peer.~:checks","Value":21},"Type":"Index"} +{"Data":{"Key":"partition.default:peer.~:namespace.default:checks","Value":21},"Type":"Index"} +{"Data":{"Key":"partition.default:peer.~:namespace.default:service.consul","Value":22},"Type":"Index"} +{"Data":{"Key":"partition.default:peer.~:namespace.default:service_kind.typical","Value":22},"Type":"Index"} +{"Data":{"Key":"partition.default:peer.~:namespace.default:services","Value":21},"Type":"Index"} +{"Data":{"Key":"partition.default:peer.~:node.mkeeler-C02F44FDMD6R","Value":22},"Type":"Index"} +{"Data":{"Key":"partition.default:peer.~:nodes","Value":22},"Type":"Index"} +{"Data":{"Key":"partition.default:peer.~:service_kind.typical","Value":22},"Type":"Index"} +{"Data":{"Key":"partition.default:peer.~:services","Value":21},"Type":"Index"} +{"Data":{"Key":"partition.default:peering","Value":62},"Type":"Index"} +{"Data":{"Key":"partition.default:peering-trust-bundles","Value":62},"Type":"Index"} +{"Data":{"Key":"partition.default:service-virtual-ips","Value":43},"Type":"Index"} +{"Data":{"Key":"partition.default:sessions","Value":29},"Type":"Index"} +{"Data":{"Key":"partition.default:namespace.default:tombstones","Value":28},"Type":"Index"} +{"Data":{"Key":"peering","Value":62},"Type":"Index"} +{"Data":{"Key":"peering-trust-bundles","Value":62},"Type":"Index"} +{"Data":{"Key":"prepared-queries","Value":30},"Type":"Index"} +{"Data":{"Key":"services","Value":21},"Type":"Index"} +{"Data":{"Key":"sessions","Value":29},"Type":"Index"} +{"Data":{"Key":"system-metadata","Value":20},"Type":"Index"} +{"Data":{"ID":"503f605c-6ac3-1bbc-4518-758e07212491","Name":"other","Partition":"default","State":1,"PeerCAPems":["-----BEGIN CERTIFICATE-----\nMIICDzCCAbWgAwIBAgIBCTAKBggqhkjOPQQDAjAxMS8wLQYDVQQDEyZwcmktMWhk\nZG90d2IuY29uc3VsLmNhLjU4NGVjZjgyLmNvbnN1bDAeFw0yNDAzMTMxNzE0MDla\nFw0zNDAzMTExNzE0MDlaMDExLzAtBgNVBAMTJnByaS0xaGRkb3R3Yi5jb25zdWwu\nY2EuNTg0ZWNmODIuY29uc3VsMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFC3D\n0+YDsc7BhFLJy5ePs5W86pEGScYI0PWilubR+NrV4o40hqgTbwANieTjd77oQTYr\nXD+uaPj+pIOnuOu5LqOBvTCBujAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUw\nAwEB/zApBgNVHQ4EIgQgLAzSlQKEkAhISEfhG1Y6m2uoA/jlY5kAYVGTHgMEbSMw\nKwYDVR0jBCQwIoAgLAzSlQKEkAhISEfhG1Y6m2uoA/jlY5kAYVGTHgMEbSMwPwYD\nVR0RBDgwNoY0c3BpZmZlOi8vNTg0ZWNmODItZmU5YS00NDY0LTVkZWItMjU5ZTRh\nYmExZGVlLmNvbnN1bDAKBggqhkjOPQQDAgNIADBFAiEAzD28+wMMO3RPfYR4kF9G\ncrqWkBCWP7k2fvwX10vLdNwCIBKf0pWS1qTOgJDxX1dn9+vqk1flv7stwwgFgJlH\nT0xb\n-----END CERTIFICATE-----\n"],"CreateIndex":59,"ModifyIndex":62,"Remote":{"Partition":"default","Datacenter":"dc1"}},"Type":"Peering"} +{"Data":{"TrustDomain":"584ecf82-fe9a-4464-5deb-259e4aba1dee.consul","PeerName":"other","Partition":"default","RootPEMs":["-----BEGIN CERTIFICATE-----\nMIICDzCCAbWgAwIBAgIBCTAKBggqhkjOPQQDAjAxMS8wLQYDVQQDEyZwcmktMWhk\nZG90d2IuY29uc3VsLmNhLjU4NGVjZjgyLmNvbnN1bDAeFw0yNDAzMTMxNzE0MDla\nFw0zNDAzMTExNzE0MDlaMDExLzAtBgNVBAMTJnByaS0xaGRkb3R3Yi5jb25zdWwu\nY2EuNTg0ZWNmODIuY29uc3VsMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFC3D\n0+YDsc7BhFLJy5ePs5W86pEGScYI0PWilubR+NrV4o40hqgTbwANieTjd77oQTYr\nXD+uaPj+pIOnuOu5LqOBvTCBujAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUw\nAwEB/zApBgNVHQ4EIgQgLAzSlQKEkAhISEfhG1Y6m2uoA/jlY5kAYVGTHgMEbSMw\nKwYDVR0jBCQwIoAgLAzSlQKEkAhISEfhG1Y6m2uoA/jlY5kAYVGTHgMEbSMwPwYD\nVR0RBDgwNoY0c3BpZmZlOi8vNTg0ZWNmODItZmU5YS00NDY0LTVkZWItMjU5ZTRh\nYmExZGVlLmNvbnN1bDAKBggqhkjOPQQDAgNIADBFAiEAzD28+wMMO3RPfYR4kF9G\ncrqWkBCWP7k2fvwX10vLdNwCIBKf0pWS1qTOgJDxX1dn9+vqk1flv7stwwgFgJlH\nT0xb\n-----END CERTIFICATE-----\n"],"ExportedPartition":"default","CreateIndex":62,"ModifyIndex":62},"Type":"PeeringTrustBundle"} +{"Data":{"PeerID":"503f605c-6ac3-1bbc-4518-758e07212491","stream":{"ActiveSecretID":"cda5e0b8-be51-5b62-a89d-8546f9c48da9"}},"Type":"PeeringSecret"} +{"Data":{"ChunkMap":{}},"Type":"ChunkingState"} diff --git a/command/snapshot/decode/testdata/TestSnapshotDecodeCommand/no-kv.ce.golden b/command/snapshot/decode/testdata/TestSnapshotDecodeCommand/no-kv.ce.golden new file mode 100644 index 0000000000..99536ad585 --- /dev/null +++ b/command/snapshot/decode/testdata/TestSnapshotDecodeCommand/no-kv.ce.golden @@ -0,0 +1,23 @@ +{"Data":{"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},"Type":"SnapshotHeader"} +{"Data":{"Datacenter":"dc1","ID":"a577b288-b354-770e-e909-da0972eb20e8","Node":"macbook-pro.lan","Address":"127.0.0.1","TaggedAddresses":{"lan":"127.0.0.1","lan_ipv4":"127.0.0.1","wan":"127.0.0.1","wan_ipv4":"127.0.0.1"},"NodeMeta":{"consul-network-segment":""},"Service":null,"Check":null,"Checks":null,"Locality":null,"SkipNodeUpdate":false,"PeerName":"","Token":"","CreateIndex":11,"ModifyIndex":12},"Type":"Register"} +{"Data":{"Datacenter":"dc1","ID":"a577b288-b354-770e-e909-da0972eb20e8","Node":"macbook-pro.lan","Address":"127.0.0.1","TaggedAddresses":{"lan":"127.0.0.1","lan_ipv4":"127.0.0.1","wan":"127.0.0.1","wan_ipv4":"127.0.0.1"},"NodeMeta":{"consul-network-segment":""},"Service":{"ID":"consul","Service":"consul","Tags":null,"Address":"","Meta":{"non_voter":"false","raft_version":"3","serf_protocol_current":"2","serf_protocol_max":"5","serf_protocol_min":"1","version":"1.9.0"},"Port":8300,"Weights":{"Passing":1,"Warning":1},"EnableTagOverride":false,"Proxy":{"Mode":"","MeshGateway":{},"Expose":{}},"Connect":{},"PeerName":"","CreateIndex":11,"ModifyIndex":11},"Check":null,"Checks":null,"Locality":null,"SkipNodeUpdate":false,"PeerName":"","Token":"","CreateIndex":11,"ModifyIndex":12},"Type":"Register"} +{"Data":{"Datacenter":"dc1","ID":"a577b288-b354-770e-e909-da0972eb20e8","Node":"macbook-pro.lan","Address":"127.0.0.1","TaggedAddresses":{"lan":"127.0.0.1","lan_ipv4":"127.0.0.1","wan":"127.0.0.1","wan_ipv4":"127.0.0.1"},"NodeMeta":{"consul-network-segment":""},"Service":null,"Check":{"Node":"macbook-pro.lan","CheckID":"serfHealth","Name":"Serf Health Status","Status":"passing","Notes":"","Output":"Agent alive and reachable","ServiceID":"","ServiceName":"","ServiceTags":null,"Type":"","Interval":"","Timeout":"","ExposedPort":0,"Definition":{},"CreateIndex":11,"ModifyIndex":11},"Checks":null,"Locality":null,"SkipNodeUpdate":false,"PeerName":"","Token":"","CreateIndex":11,"ModifyIndex":12},"Type":"Register"} +{"Data":{"Datacenter":"","Config":{"CleanupDeadServers":false,"LastContactThreshold":0,"MaxTrailingLogs":0,"MinQuorum":0,"ServerStabilizationTime":0,"RedundancyZoneTag":"","DisableUpgradeMigration":false,"UpgradeVersionTag":"","CreateIndex":0,"ModifyIndex":0},"CAS":false,"Token":""},"Type":"Autopilot"} +{"Data":{"Op":"","Datacenter":"","Index":0,"Roots":null,"Config":null,"ProviderState":null,"Token":""},"Type":"ConnectCA"} +{"Data":{"ID":"07:80:c8:de:f6:41:86:29:8f:9c:b8:17:d6:48:c2:d5:c5:5c:7f:0c:03:f7:cf:97:5a:a7:c1:68:aa:23:ae:81","PrivateKey":"-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIGwMuojUj9fSERmBjUjp3AbmkFhwRZZEEcm0XgdnCSyRoAoGCCqGSM49\nAwEHoUQDQgAEBi0AvynqnFMRIsxs1VNKkDBIvlKywpuFIaZY3TDMEvuVV/iLvFOl\n8I/OBkjXVsUPzXpao6Da6oPpbtzVuphD7A==\n-----END EC PRIVATE KEY-----\n","RootCert":"-----BEGIN CERTIFICATE-----\nMIICDjCCAbOgAwIBAgIBBzAKBggqhkjOPQQDAjAwMS4wLAYDVQQDEyVwcmktMXN5\nYzU4Zi5jb25zdWwuY2EuMzMzZDFmYTIuY29uc3VsMB4XDTIwMTAwOTA1NDUyNloX\nDTMwMTAwOTA1NDUyNlowMDEuMCwGA1UEAxMlcHJpLTFzeWM1OGYuY29uc3VsLmNh\nLjMzM2QxZmEyLmNvbnN1bDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABAYtAL8p\n6pxTESLMbNVTSpAwSL5SssKbhSGmWN0wzBL7lVf4i7xTpfCPzgZI11bFD816WqOg\n2uqD6W7c1bqYQ+yjgb0wgbowDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB\nAf8wKQYDVR0OBCIEIOZNpB8CrBdX2haue5rcSaauDsPs8S1Ld39j9ne1JP+xMCsG\nA1UdIwQkMCKAIOZNpB8CrBdX2haue5rcSaauDsPs8S1Ld39j9ne1JP+xMD8GA1Ud\nEQQ4MDaGNHNwaWZmZTovLzMzM2QxZmEyLTFjMTItMzgzYS05MDRlLWFmMjc0OTEz\nNmViZS5jb25zdWwwCgYIKoZIzj0EAwIDSQAwRgIhALy+D4SiHnHaEtxj62ncLyus\nxf6XS4amT2P3uc+zsURWAiEAvGAvh5/BwyUg0wzcVGZ40K+tllAVmk4fWL6dkUcT\nJ+o=\n-----END CERTIFICATE-----\n","IntermediateCert":"","CreateIndex":6,"ModifyIndex":8},"Type":"ConnectCAProviderState"} +{"Data":{"Provider":"consul","Config":{"IntermediateCertTTL":"8760h","LeafCertTTL":"72h","RotationPeriod":"2160h"},"State":null,"ForceWithoutCrossSigning":false,"CreateIndex":5,"ModifyIndex":5},"Type":"ConnectCAConfig"} +{"Data":{"Datacenter":"","Op":"upsert","State":{"Datacenter":"dc1","UpdatedAt":"2020-10-09T05:45:26.48447Z","PrimaryModifyIndex":13,"CreateIndex":13,"ModifyIndex":13},"Token":""},"Type":"FederationState"} +{"Data":{"Key":"intention-format","Value":"config-entry","CreateIndex":10,"ModifyIndex":10},"Type":"SystemMetadata"} +{"Data":{"Key":"checks","Value":11},"Type":"Index"} +{"Data":{"Key":"config-entries","Value":10},"Type":"Index"} +{"Data":{"Key":"connect-ca-builtin","Value":8},"Type":"Index"} +{"Data":{"Key":"connect-ca-builtin-serial","Value":7},"Type":"Index"} +{"Data":{"Key":"connect-ca-roots","Value":9},"Type":"Index"} +{"Data":{"Key":"connect-intentions","Value":10},"Type":"Index"} +{"Data":{"Key":"federation-states","Value":13},"Type":"Index"} +{"Data":{"Key":"nodes","Value":12},"Type":"Index"} +{"Data":{"Key":"service.consul","Value":12},"Type":"Index"} +{"Data":{"Key":"service_kind.typical","Value":12},"Type":"Index"} +{"Data":{"Key":"services","Value":11},"Type":"Index"} +{"Data":{"Key":"system-metadata","Value":10},"Type":"Index"} +{"Data":{"ChunkMap":{}},"Type":"ChunkingState"} diff --git a/command/snapshot/decode/testdata/TestSnapshotDecodeCommand/with-kv.ce.golden b/command/snapshot/decode/testdata/TestSnapshotDecodeCommand/with-kv.ce.golden new file mode 100644 index 0000000000..62c95700a4 --- /dev/null +++ b/command/snapshot/decode/testdata/TestSnapshotDecodeCommand/with-kv.ce.golden @@ -0,0 +1,51 @@ +{"Data":{"Version":1,"ID":"2-12426-1604593650375","Index":12426,"Term":2,"Peers":"kbMxOTIuMTY4Ljg2LjIzNjo4MzAw","Configuration":{"Servers":[{"Suffrage":0,"ID":"1047e34e-dc15-fa4b-8e1d-5bf74c2ac239","Address":"192.168.86.236:8300"}]},"ConfigurationIndex":1,"Size":17228},"Type":"SnapshotHeader"} +{"Data":{"Datacenter":"dc1","ID":"1047e34e-dc15-fa4b-8e1d-5bf74c2ac239","Node":"hashicorp.lan","Address":"192.168.86.236","TaggedAddresses":{"lan":"192.168.86.236","lan_ipv4":"192.168.86.236","wan":"192.168.86.236","wan_ipv4":"192.168.86.236"},"NodeMeta":{"consul-network-segment":""},"Service":null,"Check":null,"Checks":null,"Locality":null,"SkipNodeUpdate":false,"PeerName":"","Token":"","CreateIndex":6,"ModifyIndex":7},"Type":"Register"} +{"Data":{"Datacenter":"dc1","ID":"1047e34e-dc15-fa4b-8e1d-5bf74c2ac239","Node":"hashicorp.lan","Address":"192.168.86.236","TaggedAddresses":{"lan":"192.168.86.236","lan_ipv4":"192.168.86.236","wan":"192.168.86.236","wan_ipv4":"192.168.86.236"},"NodeMeta":{"consul-network-segment":""},"Service":{"ID":"consul","Service":"consul","Tags":null,"Address":"","Meta":{"non_voter":"false","raft_version":"3","serf_protocol_current":"2","serf_protocol_max":"5","serf_protocol_min":"1","version":"1.8.5"},"Port":8300,"Weights":{"Passing":1,"Warning":1},"EnableTagOverride":false,"Proxy":{"Mode":"","MeshGateway":{},"Expose":{}},"Connect":{},"PeerName":"","CreateIndex":6,"ModifyIndex":6},"Check":null,"Checks":null,"Locality":null,"SkipNodeUpdate":false,"PeerName":"","Token":"","CreateIndex":6,"ModifyIndex":7},"Type":"Register"} +{"Data":{"Datacenter":"dc1","ID":"1047e34e-dc15-fa4b-8e1d-5bf74c2ac239","Node":"hashicorp.lan","Address":"192.168.86.236","TaggedAddresses":{"lan":"192.168.86.236","lan_ipv4":"192.168.86.236","wan":"192.168.86.236","wan_ipv4":"192.168.86.236"},"NodeMeta":{"consul-network-segment":""},"Service":{"ID":"vault:127.0.0.1:8200","Service":"vault","Tags":["active","initialized"],"Address":"127.0.0.1","TaggedAddresses":{"lan_ipv4":{"Address":"127.0.0.1","Port":8200},"wan_ipv4":{"Address":"127.0.0.1","Port":8200}},"Meta":null,"Port":8200,"Weights":{"Passing":1,"Warning":1},"EnableTagOverride":false,"Proxy":{"Mode":"","MeshGateway":{},"Expose":{}},"Connect":{},"PeerName":"","CreateIndex":3470,"ModifyIndex":3497},"Check":null,"Checks":null,"Locality":null,"SkipNodeUpdate":false,"PeerName":"","Token":"","CreateIndex":6,"ModifyIndex":7},"Type":"Register"} +{"Data":{"Datacenter":"dc1","ID":"1047e34e-dc15-fa4b-8e1d-5bf74c2ac239","Node":"hashicorp.lan","Address":"192.168.86.236","TaggedAddresses":{"lan":"192.168.86.236","lan_ipv4":"192.168.86.236","wan":"192.168.86.236","wan_ipv4":"192.168.86.236"},"NodeMeta":{"consul-network-segment":""},"Service":null,"Check":{"Node":"hashicorp.lan","CheckID":"serfHealth","Name":"Serf Health Status","Status":"passing","Notes":"","Output":"Agent alive and reachable","ServiceID":"","ServiceName":"","ServiceTags":null,"Type":"","Interval":"","Timeout":"","ExposedPort":0,"Definition":{},"CreateIndex":6,"ModifyIndex":6},"Checks":null,"Locality":null,"SkipNodeUpdate":false,"PeerName":"","Token":"","CreateIndex":6,"ModifyIndex":7},"Type":"Register"} +{"Data":{"Datacenter":"dc1","ID":"1047e34e-dc15-fa4b-8e1d-5bf74c2ac239","Node":"hashicorp.lan","Address":"192.168.86.236","TaggedAddresses":{"lan":"192.168.86.236","lan_ipv4":"192.168.86.236","wan":"192.168.86.236","wan_ipv4":"192.168.86.236"},"NodeMeta":{"consul-network-segment":""},"Service":null,"Check":{"Node":"hashicorp.lan","CheckID":"vault:127.0.0.1:8200:vault-sealed-check","Name":"Vault Sealed Status","Status":"passing","Notes":"Vault service is healthy when Vault is in an unsealed status and can become an active Vault server","Output":"Vault Unsealed","ServiceID":"vault:127.0.0.1:8200","ServiceName":"vault","ServiceTags":["active","initialized"],"Type":"ttl","Interval":"","Timeout":"","ExposedPort":0,"Definition":{},"CreateIndex":3471,"ModifyIndex":6836},"Checks":null,"Locality":null,"SkipNodeUpdate":false,"PeerName":"","Token":"","CreateIndex":6,"ModifyIndex":7},"Type":"Register"} +{"Data":[{"Node":"hashicorp.lan","Segment":"","Coord":{"Vec":[0,0,0,0,0,0,0,0],"Error":1.5,"Adjustment":0,"Height":0.00001}}],"Type":"CoordinateBatchUpdate"} +{"Data":{"Datacenter":"","Op":"","Session":{"ID":"","Name":"","Node":"","LockDelay":0,"Behavior":"","TTL":"","NodeChecks":null,"ServiceChecks":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"Session"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","DirEnt":{"LockIndex":0,"Key":"","Flags":0,"Value":null,"CreateIndex":0,"ModifyIndex":0},"Token":""},"Type":"KVS"} +{"Data":{"Datacenter":"","Op":"","ReapIndex":0,"Token":""},"Type":"Tombstone"} +{"Data":{"Datacenter":"","Op":"","ReapIndex":0,"Token":""},"Type":"Tombstone"} +{"Data":{"Datacenter":"","Config":{"CleanupDeadServers":false,"LastContactThreshold":0,"MaxTrailingLogs":0,"MinQuorum":0,"ServerStabilizationTime":0,"RedundancyZoneTag":"","DisableUpgradeMigration":false,"UpgradeVersionTag":"","CreateIndex":0,"ModifyIndex":0},"CAS":false,"Token":""},"Type":"Autopilot"} +{"Data":{"Datacenter":"","Op":"upsert","State":{"Datacenter":"dc1","UpdatedAt":"2020-11-03T23:57:19.636962Z","PrimaryModifyIndex":5,"CreateIndex":5,"ModifyIndex":5},"Token":""},"Type":"FederationState"} +{"Data":{"Key":"checks","Value":6836},"Type":"Index"} +{"Data":{"Key":"coordinates","Value":12424},"Type":"Index"} +{"Data":{"Key":"federation-states","Value":5},"Type":"Index"} +{"Data":{"Key":"kvs","Value":12426},"Type":"Index"} +{"Data":{"Key":"nodes","Value":7},"Type":"Index"} +{"Data":{"Key":"service.consul","Value":7},"Type":"Index"} +{"Data":{"Key":"service.vault","Value":6836},"Type":"Index"} +{"Data":{"Key":"service_kind.typical","Value":6836},"Type":"Index"} +{"Data":{"Key":"services","Value":3497},"Type":"Index"} +{"Data":{"Key":"sessions","Value":3493},"Type":"Index"} +{"Data":{"Key":"tombstones","Value":12426},"Type":"Index"} +{"Data":{"ChunkMap":{}},"Type":"ChunkingState"} diff --git a/command/snapshot/decode/testdata/all.snap b/command/snapshot/decode/testdata/all.snap new file mode 100644 index 0000000000..8b69dec197 Binary files /dev/null and b/command/snapshot/decode/testdata/all.snap differ diff --git a/command/snapshot/decode/testdata/backup.snap b/command/snapshot/decode/testdata/backup.snap new file mode 100644 index 0000000000..ddb621c396 Binary files /dev/null and b/command/snapshot/decode/testdata/backup.snap differ diff --git a/command/snapshot/decode/testdata/backupWithKV.snap b/command/snapshot/decode/testdata/backupWithKV.snap new file mode 100644 index 0000000000..453b3a03f5 Binary files /dev/null and b/command/snapshot/decode/testdata/backupWithKV.snap differ diff --git a/command/snapshot/decode/testdata/populate-all.sh b/command/snapshot/decode/testdata/populate-all.sh new file mode 100755 index 0000000000..dfecec7249 --- /dev/null +++ b/command/snapshot/decode/testdata/populate-all.sh @@ -0,0 +1,296 @@ +#!/bin/bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +set -e + +trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT + + +readonly SCRIPT_NAME="$(basename ${BASH_SOURCE[0]})" +readonly SCRIPT_DIR="$(dirname ${BASH_SOURCE[0]})" + +# Start a couple dev agents in the background +echo "Starting Dev Agents" +consul agent -dev -hcl 'acl { enabled = true default_policy="allow" tokens { initial_management = "root" } }' >/dev/null 2>&1 & +consul agent -dev -dns-port=9600 -grpc-port=9502 -grpc-tls-port=9503 -http-port=9500 -serf-lan-port=9301 -serf-wan-port=9302 -server-port=9300 >/dev/null 2>&1 & + +# should be long enough for the dev agents to be available +sleep 5 + +# This script expects a consul dev agent with acls enabled in default allow to be running on localhost +# consul agent -dev -hcl 'acl { enabled = true default_policy="allow" tokens { initial_management = "root" } }' +# It also requires another dev agent running on alternative ports to peer with +# consul agent -dev -dns-port=9600 -grpc-port=9502 -grpc-tls-port=9503 -http-port=9500 -serf-lan-port=9301 -serf-wan-port=9302 -server-port=9300 + +# Just running Consul will cause the following data to be in the snapshot: +# Register +# ConnectCA +# ConnectCAProviderState +# ConnectCAConfig +# Autopilot +# Index +# SystemMetadata +# CoordinateBatchUpdate +# FederationState +# ChunkingState +# FreeVirtualIP +# Partition +# Tombstone + +# Ensure a KV entry ends up in the snapshot +echo "Creating KV Entry" +consul kv put foo/bar 1 >/dev/null + +# Ensure a tombstone ends up in the snapshot +echo "Forcing KV Tombstone Creation" +consul kv put foo/baz 2 >/dev/null +consul kv delete foo/baz > /dev/null + + +# Ensure a session ends up in the snapshot +echo "Creating Session" +curl -s -X PUT localhost:8500/v1/session/create >/dev/null + +# Ensure a prepared query ends up in the snapshot +echo "Creating Prepared Query" +curl -s -X POST localhost:8500/v1/query -d '{"Name": "test", "Token": "root", "Service": {"Service": "test"}}' >/dev/null + +# Ensure an ACL token ends up in the snapshot +echo "Creating ACL Token" +consul acl token create -node-identity=localhost:dc1 >/dev/null + +# Ensure an ACL policy ends up in the snapshot +echo "Creating ACL Policy" +consul acl policy create -name=test -rules='node_prefix "" { policy = "write" }' >/dev/null + +# Ensure an ACL role ends up in the snapshot +echo "Creating ACL Role" +consul acl role create -name=test -policy-name=test >/dev/null + +# Ensure an ACL auth method ends up in the snapshot +echo "Creating ACL Auth Method" +consul acl auth-method create -type jwt -name test -config '{"JWTValidationPubKeys": ["-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENRw6ZwlBOx5XZKjcc1HhU00sDehc\n8nqeeSnRZLv89yT7M7qUOFDtR29FR/AFUSAEOFl1iIYLqNMElHs2VkgAZA==\n-----END PUBLIC KEY-----"]}' >/dev/null + +# Ensure an ACL binding rule ends up in the snapshot +echo "Creating ACL Binding Rule" +consul acl binding-rule create -bind-type="service" -bind-name="service" -method="test" >/dev/null + +# Ensure config entries end up in the snapshot +echo "Creating Proxy Default Config Entry" +consul config write - >/dev/null </dev/null </dev/null </dev/null </dev/null </dev/null </dev/null </dev/null </dev/null </dev/null </dev/null </dev/null </dev/null </dev/null +if [ $? -eq 0 ]; then +set -e +echo "Creating Sameness Group Config Entry" +consul config write - >/dev/null </dev/null </dev/null </dev/null </dev/null + +echo "Saving Snapshot to all.snap" +sleep 2 +consul snapshot save "${SCRIPT_DIR}/all.snap" >/dev/null \ No newline at end of file diff --git a/command/snapshot/snapshot_command.go b/command/snapshot/snapshot_command.go index 2e96550e19..e5c4805e7f 100644 --- a/command/snapshot/snapshot_command.go +++ b/command/snapshot/snapshot_command.go @@ -45,6 +45,10 @@ Usage: consul snapshot [options] [args] Restore a snapshot: $ consul snapshot restore backup.snap + + Decode a snapshot: + + $ consul snapshot decode backup.snap Inspect a snapshot: diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/destination_multiport_test.go b/internal/mesh/internal/controllers/sidecarproxy/builder/destination_multiport_test.go index 9e77bba692..7f64f4b5ad 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/destination_multiport_test.go +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/destination_multiport_test.go @@ -250,7 +250,7 @@ func TestBuildMultiportImplicitDestinations(t *testing.T) { }) actual := protoToJSON(t, proxyTmpl) - expected := JSONToProxyTemplate(t, golden.GetBytes(t, actual, name+"-"+tenancy.Partition+"-"+tenancy.Namespace+".golden")) + expected := JSONToProxyTemplate(t, golden.GetBytes(t, []byte(actual), name+"-"+tenancy.Partition+"-"+tenancy.Namespace+".golden")) // sort routers on listener from golden file expectedRouters := expected.ProxyState.Listeners[0].Routers diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_multiport_test.go b/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_multiport_test.go index 4a60d0f8b3..e9fcb1fff5 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_multiport_test.go +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_multiport_test.go @@ -157,7 +157,7 @@ func TestBuildLocalApp_Multiport(t *testing.T) { }) actual := protoToJSON(t, proxyTmpl) - expected := JSONToProxyTemplate(t, golden.GetBytes(t, actual, name+"-"+tenancy.Partition+"-"+tenancy.Namespace+".golden")) + expected := JSONToProxyTemplate(t, golden.GetBytes(t, []byte(actual), name+"-"+tenancy.Partition+"-"+tenancy.Namespace+".golden")) // sort routers on listener from golden file expectedRouters := expected.ProxyState.Listeners[0].Routers diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_test.go b/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_test.go index 2fa095da11..1038cee9a0 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_test.go +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_test.go @@ -115,7 +115,7 @@ func TestBuildLocalApp(t *testing.T) { }) actual := protoToJSON(t, proxyTmpl) - expected := JSONToProxyTemplate(t, golden.GetBytes(t, actual, name+"-"+tenancy.Partition+"-"+tenancy.Namespace+".golden")) + expected := JSONToProxyTemplate(t, golden.GetBytes(t, []byte(actual), name+"-"+tenancy.Partition+"-"+tenancy.Namespace+".golden")) // sort routers on listener from golden file expectedRouters := expected.ProxyState.Listeners[0].Routers @@ -233,7 +233,7 @@ func TestBuildLocalApp_WithProxyConfiguration(t *testing.T) { }) actual := protoToJSON(t, proxyTmpl) - expected := JSONToProxyTemplate(t, golden.GetBytes(t, actual, name+"-"+tenancy.Partition+"-"+tenancy.Namespace+".golden")) + expected := JSONToProxyTemplate(t, golden.GetBytes(t, []byte(actual), name+"-"+tenancy.Partition+"-"+tenancy.Namespace+".golden")) // sort routers on listener from golden file expectedRouters := expected.ProxyState.Listeners[0].Routers diff --git a/internal/testing/golden/golden.go b/internal/testing/golden/golden.go index a4d971d0d0..7146261a7e 100644 --- a/internal/testing/golden/golden.go +++ b/internal/testing/golden/golden.go @@ -22,7 +22,7 @@ var update = flag.Bool("update", false, "update golden files") // to the value of actual. func Get(t *testing.T, actual, filename string) string { t.Helper() - return string(GetBytes(t, actual, filename)) + return string(GetBytes(t, []byte(actual), filename)) } // GetBytes reads the expected value from the file at filename and returns the @@ -30,7 +30,7 @@ func Get(t *testing.T, actual, filename string) string { // // If the `-update` flag is used with `go test`, the golden file will be updated // to the value of actual. -func GetBytes(t *testing.T, actual, filename string) []byte { +func GetBytes(t *testing.T, actual []byte, filename string) []byte { t.Helper() path := filepath.Join("testdata", filename) @@ -38,7 +38,7 @@ func GetBytes(t *testing.T, actual, filename string) []byte { if dir := filepath.Dir(path); dir != "." { require.NoError(t, os.MkdirAll(dir, 0755)) } - err := os.WriteFile(path, []byte(actual), 0644) + err := os.WriteFile(path, actual, 0644) require.NoError(t, err) }