Allow dialer to re-establish terminated peering (#16776)

Currently, if an acceptor peer deletes a peering the dialer's peering
will eventually get to a "terminated" state. If the two clusters need to
be re-peered the acceptor will re-generate the token but the dialer will
encounter this error on the call to establish:

"failed to get addresses to dial peer: failed to refresh peer server
addresses, will continue to use initial addresses: there is no active
peering for "<<<ID>>>""

This is because in `exchangeSecret().GetDialAddresses()` we will get an
error if fetching addresses for an inactive peering. The peering shows
up as inactive at this point because of the existing terminated state.

Rather than checking whether a peering is active we can instead check
whether it was deleted. This way users do not need to delete terminated
peerings in the dialing cluster before re-establishing them.
This commit is contained in:
Freddy 2023-04-03 12:07:45 -06:00 committed by GitHub
parent bef1aafbf9
commit 04e6e79b09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 53 additions and 4 deletions

3
.changelog/16776.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
peering: allow re-establishing terminated peering from new token without deleting existing peering first.
```

View File

@ -472,6 +472,30 @@ func TestLeader_PeeringSync_Lifecycle_ServerDeletion(t *testing.T) {
require.NoError(r, err) require.NoError(r, err)
require.Equal(r, pbpeering.PeeringState_TERMINATED, peering.State) require.Equal(r, pbpeering.PeeringState_TERMINATED, peering.State)
}) })
// Re-establishing a peering terminated by the acceptor should be possible
// without needing to delete the terminated peering first.
ctx, cancel = context.WithTimeout(context.Background(), 3*time.Second)
t.Cleanup(cancel)
req = pbpeering.GenerateTokenRequest{
PeerName: "my-peer-dialer",
}
resp, err = peeringClient.GenerateToken(ctx, &req)
require.NoError(t, err)
tokenJSON, err = base64.StdEncoding.DecodeString(resp.PeeringToken)
require.NoError(t, err)
token = structs.PeeringToken{}
require.NoError(t, json.Unmarshal(tokenJSON, &token))
establishReq = pbpeering.EstablishRequest{
PeerName: "my-peer-acceptor",
PeeringToken: resp.PeeringToken,
}
_, err = dialerClient.Establish(ctx, &establishReq)
require.NoError(t, err)
} }
func TestLeader_PeeringSync_FailsForTLSError(t *testing.T) { func TestLeader_PeeringSync_FailsForTLSError(t *testing.T) {

View File

@ -150,8 +150,11 @@ func (b *PeeringBackend) fetchPeerServerAddresses(ws memdb.WatchSet, peerID stri
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to fetch peer %q: %w", peerID, err) return nil, fmt.Errorf("failed to fetch peer %q: %w", peerID, err)
} }
if !peering.IsActive() { if peering == nil {
return nil, fmt.Errorf("there is no active peering for %q", peerID) return nil, fmt.Errorf("unknown peering %q", peerID)
}
if peering.DeletedAt != nil && !structs.IsZeroProtoTime(peering.DeletedAt) {
return nil, fmt.Errorf("peering %q was deleted", peerID)
} }
return bufferFromAddresses(peering.GetAddressesToDial()) return bufferFromAddresses(peering.GetAddressesToDial())
} }

View File

@ -256,7 +256,7 @@ func TestPeeringBackend_GetDialAddresses(t *testing.T) {
}, },
peerID: acceptorPeerID, peerID: acceptorPeerID,
expect: expectation{ expect: expectation{
err: fmt.Sprintf(`there is no active peering for %q`, acceptorPeerID), err: fmt.Sprintf(`unknown peering %q`, acceptorPeerID),
}, },
}, },
{ {
@ -387,6 +387,25 @@ func TestPeeringBackend_GetDialAddresses(t *testing.T) {
gatewayAddrs: []string{"5.6.7.8:8443", "6.7.8.9:8443"}, gatewayAddrs: []string{"5.6.7.8:8443", "6.7.8.9:8443"},
}, },
}, },
{
name: "addresses are returned if the peering is marked as terminated",
setup: func(store *state.Store) {
require.NoError(t, store.PeeringWrite(5, &pbpeering.PeeringWriteRequest{
Peering: &pbpeering.Peering{
Name: "dialer",
ID: dialerPeerID,
PeerServerAddresses: []string{"1.2.3.4:8502", "2.3.4.5:8503"},
State: pbpeering.PeeringState_TERMINATED,
},
}))
},
peerID: dialerPeerID,
expect: expectation{
// Gateways come first, and we use their LAN addresses since this is for outbound communication.
addrs: []string{"5.6.7.8:8443", "6.7.8.9:8443", "1.2.3.4:8502", "2.3.4.5:8503"},
gatewayAddrs: []string{"5.6.7.8:8443", "6.7.8.9:8443"},
},
},
{ {
name: "addresses are not returned if the peering is deleted", name: "addresses are not returned if the peering is deleted",
setup: func(store *state.Store) { setup: func(store *state.Store) {
@ -404,7 +423,7 @@ func TestPeeringBackend_GetDialAddresses(t *testing.T) {
}, },
peerID: dialerPeerID, peerID: dialerPeerID,
expect: expectation{ expect: expectation{
err: fmt.Sprintf(`there is no active peering for %q`, dialerPeerID), err: fmt.Sprintf(`peering %q was deleted`, dialerPeerID),
}, },
}, },
} }