Merge branch 'main' of ssh://github.com/hashicorp/consul
This commit is contained in:
commit
ae1be7dcbb
|
@ -0,0 +1,3 @@
|
|||
```release-note:bug
|
||||
agent: Fixed a compatibility issue when restoring snapshots from pre-1.13.0 versions of Consul [[GH-14107](https://github.com/hashicorp/consul/issues/14107)]
|
||||
```
|
|
@ -1,8 +1,12 @@
|
|||
package fsm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/hashicorp/consul-net-rpc/go-msgpack/codec"
|
||||
"github.com/hashicorp/raft"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
|
||||
"github.com/hashicorp/consul/agent/consul/state"
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
|
@ -886,11 +890,43 @@ func restoreSystemMetadata(header *SnapshotHeader, restore *state.Restore, decod
|
|||
}
|
||||
|
||||
func restoreServiceVirtualIP(header *SnapshotHeader, restore *state.Restore, decoder *codec.Decoder) error {
|
||||
var req state.ServiceVirtualIP
|
||||
// state.ServiceVirtualIP was changed in a breaking way in 1.13.0 (2e4cb6f77d2be36b02e9be0b289b24e5b0afb794).
|
||||
// We attempt to reconcile the older type by decoding to a map then decoding that map into
|
||||
// structs.PeeredServiceName first, and then structs.ServiceName.
|
||||
var req struct {
|
||||
Service map[string]interface{}
|
||||
IP net.IP
|
||||
|
||||
structs.RaftIndex
|
||||
}
|
||||
if err := decoder.Decode(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := restore.ServiceVirtualIP(req); err != nil {
|
||||
|
||||
vip := state.ServiceVirtualIP{
|
||||
IP: req.IP,
|
||||
RaftIndex: req.RaftIndex,
|
||||
}
|
||||
|
||||
// PeeredServiceName is the expected primary key type.
|
||||
var psn structs.PeeredServiceName
|
||||
if err := mapstructure.Decode(req.Service, &psn); err != nil {
|
||||
return fmt.Errorf("cannot decode to structs.PeeredServiceName: %w", err)
|
||||
}
|
||||
vip.Service = psn
|
||||
|
||||
// If the expected primary key field is empty, it must be the older ServiceName type.
|
||||
if vip.Service.ServiceName.Name == "" {
|
||||
var sn structs.ServiceName
|
||||
if err := mapstructure.Decode(req.Service, &sn); err != nil {
|
||||
return fmt.Errorf("cannot decode to structs.ServiceName: %w", err)
|
||||
}
|
||||
vip.Service = structs.PeeredServiceName{
|
||||
ServiceName: sn,
|
||||
}
|
||||
}
|
||||
|
||||
if err := restore.ServiceVirtualIP(vip); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -3,6 +3,7 @@ package fsm
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -962,3 +963,66 @@ func TestFSM_BadSnapshot_NilCAConfig(t *testing.T) {
|
|||
require.EqualValues(t, 0, idx)
|
||||
require.Nil(t, config)
|
||||
}
|
||||
|
||||
// This test asserts that ServiceVirtualIP, which made a breaking change
|
||||
// in 1.13.0, can still restore from older snapshots which use the old
|
||||
// state.ServiceVirtualIP type.
|
||||
func Test_restoreServiceVirtualIP(t *testing.T) {
|
||||
psn := structs.PeeredServiceName{
|
||||
ServiceName: structs.ServiceName{
|
||||
Name: "foo",
|
||||
},
|
||||
}
|
||||
|
||||
run := func(t *testing.T, input interface{}) {
|
||||
t.Helper()
|
||||
|
||||
var b []byte
|
||||
buf := bytes.NewBuffer(b)
|
||||
// Encode input
|
||||
encoder := codec.NewEncoder(buf, structs.MsgpackHandle)
|
||||
require.NoError(t, encoder.Encode(input))
|
||||
|
||||
// Create a decoder
|
||||
dec := codec.NewDecoder(buf, structs.MsgpackHandle)
|
||||
|
||||
logger := testutil.Logger(t)
|
||||
fsm, err := New(nil, logger)
|
||||
require.NoError(t, err)
|
||||
|
||||
restore := fsm.State().Restore()
|
||||
|
||||
// Call restore
|
||||
require.NoError(t, restoreServiceVirtualIP(nil, restore, dec))
|
||||
require.NoError(t, restore.Commit())
|
||||
|
||||
ip, err := fsm.State().VirtualIPForService(psn)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 240->224 due to addIPOffset
|
||||
require.Equal(t, "224.0.0.2", ip)
|
||||
}
|
||||
|
||||
t.Run("new ServiceVirtualIP with PeeredServiceName", func(t *testing.T) {
|
||||
run(t, state.ServiceVirtualIP{
|
||||
Service: psn,
|
||||
IP: net.ParseIP("240.0.0.2"),
|
||||
RaftIndex: structs.RaftIndex{},
|
||||
})
|
||||
})
|
||||
t.Run("pre-1.13.0 ServiceVirtualIP with ServiceName", func(t *testing.T) {
|
||||
type compatServiceVirtualIP struct {
|
||||
Service structs.ServiceName
|
||||
IP net.IP
|
||||
RaftIndex structs.RaftIndex
|
||||
}
|
||||
|
||||
run(t, compatServiceVirtualIP{
|
||||
Service: structs.ServiceName{
|
||||
Name: "foo",
|
||||
},
|
||||
IP: net.ParseIP("240.0.0.2"),
|
||||
RaftIndex: structs.RaftIndex{},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2211,8 +2211,8 @@ type PeeredServiceName struct {
|
|||
}
|
||||
|
||||
type ServiceName struct {
|
||||
Name string
|
||||
acl.EnterpriseMeta
|
||||
Name string
|
||||
acl.EnterpriseMeta `mapstructure:",squash"`
|
||||
}
|
||||
|
||||
func NewServiceName(name string, entMeta *acl.EnterpriseMeta) ServiceName {
|
||||
|
|
Loading…
Reference in New Issue