mirror of https://github.com/hashicorp/consul
Include a content hash of the intention for use during replication
parent
747ae6bdf5
commit
4bc1277315
|
@ -141,6 +141,9 @@ func (s *Intention) Apply(
|
|||
}
|
||||
}
|
||||
|
||||
// make sure we set the hash prior to raft application
|
||||
args.Intention.SetHash(true)
|
||||
|
||||
// Commit
|
||||
resp, err := s.srv.raftApply(structs.IntentionRequestType, args)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package consul
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -411,14 +412,14 @@ func retryLoopBackoff(stopCh <-chan struct{}, loopFn func() error, errFn func(er
|
|||
// diffIntentions computes the difference between the local and remote intentions
|
||||
// and returns lists of deletes and updates.
|
||||
func diffIntentions(local, remote structs.Intentions) (structs.Intentions, structs.Intentions) {
|
||||
localIdx := make(map[string]uint64, len(local))
|
||||
localIdx := make(map[string][]byte, len(local))
|
||||
remoteIdx := make(map[string]struct{}, len(remote))
|
||||
|
||||
var deletes structs.Intentions
|
||||
var updates structs.Intentions
|
||||
|
||||
for _, intention := range local {
|
||||
localIdx[intention.ID] = intention.ModifyIndex
|
||||
localIdx[intention.ID] = intention.Hash
|
||||
}
|
||||
for _, intention := range remote {
|
||||
remoteIdx[intention.ID] = struct{}{}
|
||||
|
@ -431,10 +432,10 @@ func diffIntentions(local, remote structs.Intentions) (structs.Intentions, struc
|
|||
}
|
||||
|
||||
for _, intention := range remote {
|
||||
existingIdx, ok := localIdx[intention.ID]
|
||||
existingHash, ok := localIdx[intention.ID]
|
||||
if !ok {
|
||||
updates = append(updates, intention)
|
||||
} else if existingIdx < intention.ModifyIndex {
|
||||
} else if bytes.Compare(existingHash, intention.Hash) != 0 {
|
||||
updates = append(updates, intention)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -9,6 +11,8 @@ import (
|
|||
"github.com/hashicorp/consul/agent/cache"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/mitchellh/hashstructure"
|
||||
|
||||
"golang.org/x/crypto/blake2b"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -70,9 +74,66 @@ type Intention struct {
|
|||
// or modified.
|
||||
CreatedAt, UpdatedAt time.Time `mapstructure:"-"`
|
||||
|
||||
// Hash of the contents of the intention
|
||||
//
|
||||
// This is needed mainly for replication purposes. When replicating from
|
||||
// one DC to another keeping the content Hash will allow us to detect
|
||||
// content changes more efficiently than checking every single field
|
||||
Hash []byte
|
||||
|
||||
RaftIndex
|
||||
}
|
||||
|
||||
func (x *Intention) SetHash(force bool) []byte {
|
||||
if force || x.Hash == nil {
|
||||
hash, err := blake2b.New256(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Any non-immutable "content" fields should be involved with the
|
||||
// overall hash. The IDs are immutable which is why they aren't here.
|
||||
// The raft indices are metadata similar to the hash which is why they
|
||||
// aren't incorporated. CreateTime is similarly immutable
|
||||
//
|
||||
// The Hash is really only used for replication to determine if a token
|
||||
// has changed and should be updated locally.
|
||||
|
||||
// Write all the user set fields
|
||||
hash.Write([]byte(x.ID))
|
||||
hash.Write([]byte(x.Description))
|
||||
hash.Write([]byte(x.SourceNS))
|
||||
hash.Write([]byte(x.SourceName))
|
||||
hash.Write([]byte(x.DestinationNS))
|
||||
hash.Write([]byte(x.DestinationName))
|
||||
hash.Write([]byte(x.SourceType))
|
||||
hash.Write([]byte(x.Action))
|
||||
hash.Write([]byte(x.DefaultAddr))
|
||||
binary.Write(hash, binary.LittleEndian, x.DefaultPort)
|
||||
binary.Write(hash, binary.LittleEndian, x.Precedence)
|
||||
|
||||
// hashing the metadata
|
||||
var keys []string
|
||||
for k := range x.Meta {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
// keep them sorted to ensure hash stability
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
hash.Write([]byte(k))
|
||||
hash.Write([]byte(x.Meta[k]))
|
||||
}
|
||||
|
||||
// Finalize the hash
|
||||
hashVal := hash.Sum(nil)
|
||||
|
||||
x.Hash = hashVal
|
||||
}
|
||||
return x.Hash
|
||||
}
|
||||
|
||||
// Validate returns an error if the intention is invalid for inserting
|
||||
// or updating.
|
||||
func (x *Intention) Validate() error {
|
||||
|
|
Loading…
Reference in New Issue