2023-03-28 18:39:22 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2020-03-09 20:59:02 +00:00
|
|
|
package state
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
memdb "github.com/hashicorp/go-memdb"
|
2021-01-30 00:17:40 +00:00
|
|
|
|
|
|
|
"github.com/hashicorp/consul/agent/structs"
|
2020-03-09 20:59:02 +00:00
|
|
|
)
|
|
|
|
|
2021-01-30 00:17:40 +00:00
|
|
|
const tableFederationStates = "federation-states"
|
2020-03-09 20:59:02 +00:00
|
|
|
|
|
|
|
func federationStateTableSchema() *memdb.TableSchema {
|
|
|
|
return &memdb.TableSchema{
|
2021-01-30 00:17:40 +00:00
|
|
|
Name: tableFederationStates,
|
2020-03-09 20:59:02 +00:00
|
|
|
Indexes: map[string]*memdb.IndexSchema{
|
2021-01-30 00:17:40 +00:00
|
|
|
indexID: {
|
|
|
|
Name: indexID,
|
2020-03-09 20:59:02 +00:00
|
|
|
AllowMissing: false,
|
|
|
|
Unique: true,
|
|
|
|
Indexer: &memdb.StringFieldIndex{
|
|
|
|
Field: "Datacenter",
|
|
|
|
Lowercase: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// FederationStates is used to pull all the federation states for the snapshot.
|
|
|
|
func (s *Snapshot) FederationStates() ([]*structs.FederationState, error) {
|
2021-01-30 00:17:40 +00:00
|
|
|
configs, err := s.tx.Get(tableFederationStates, "id")
|
2020-03-09 20:59:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var ret []*structs.FederationState
|
|
|
|
for wrapped := configs.Next(); wrapped != nil; wrapped = configs.Next() {
|
|
|
|
ret = append(ret, wrapped.(*structs.FederationState))
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FederationState is used when restoring from a snapshot.
|
|
|
|
func (s *Restore) FederationState(g *structs.FederationState) error {
|
|
|
|
// Insert
|
2021-01-30 00:17:40 +00:00
|
|
|
if err := s.tx.Insert(tableFederationStates, g); err != nil {
|
2020-03-09 20:59:02 +00:00
|
|
|
return fmt.Errorf("failed restoring federation state object: %s", err)
|
|
|
|
}
|
2021-01-30 00:17:40 +00:00
|
|
|
if err := indexUpdateMaxTxn(s.tx, g.ModifyIndex, tableFederationStates); err != nil {
|
2020-03-09 20:59:02 +00:00
|
|
|
return fmt.Errorf("failed updating index: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Store) FederationStateBatchSet(idx uint64, configs structs.FederationStates) error {
|
2020-03-19 13:11:20 +00:00
|
|
|
tx := s.db.WriteTxn(idx)
|
2020-03-09 20:59:02 +00:00
|
|
|
defer tx.Abort()
|
|
|
|
|
|
|
|
for _, config := range configs {
|
2020-07-10 00:56:43 +00:00
|
|
|
if err := federationStateSetTxn(tx, idx, config); err != nil {
|
2020-03-09 20:59:02 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-02 20:34:56 +00:00
|
|
|
return tx.Commit()
|
2020-03-09 20:59:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FederationStateSet is called to do an upsert of a given federation state.
|
|
|
|
func (s *Store) FederationStateSet(idx uint64, config *structs.FederationState) error {
|
2020-03-19 13:11:20 +00:00
|
|
|
tx := s.db.WriteTxn(idx)
|
2020-03-09 20:59:02 +00:00
|
|
|
defer tx.Abort()
|
|
|
|
|
2020-07-10 00:56:43 +00:00
|
|
|
if err := federationStateSetTxn(tx, idx, config); err != nil {
|
2020-03-09 20:59:02 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-06-02 20:34:56 +00:00
|
|
|
return tx.Commit()
|
2020-03-09 20:59:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// federationStateSetTxn upserts a federation state inside of a transaction.
|
2021-03-08 22:41:10 +00:00
|
|
|
func federationStateSetTxn(tx WriteTxn, idx uint64, config *structs.FederationState) error {
|
2020-03-09 20:59:02 +00:00
|
|
|
if config.Datacenter == "" {
|
|
|
|
return fmt.Errorf("missing datacenter on federation state")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for existing.
|
|
|
|
var existing *structs.FederationState
|
2021-01-30 00:17:40 +00:00
|
|
|
existingRaw, err := tx.First(tableFederationStates, "id", config.Datacenter)
|
2020-03-09 20:59:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed federation state lookup: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if existingRaw != nil {
|
|
|
|
existing = existingRaw.(*structs.FederationState)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the indexes
|
|
|
|
if existing != nil {
|
|
|
|
config.CreateIndex = existing.CreateIndex
|
|
|
|
config.ModifyIndex = idx
|
|
|
|
} else {
|
|
|
|
config.CreateIndex = idx
|
|
|
|
config.ModifyIndex = idx
|
|
|
|
}
|
|
|
|
|
|
|
|
if config.PrimaryModifyIndex == 0 {
|
|
|
|
// Since replication ordinarily would set this value for us, we can
|
|
|
|
// assume this is a write to the primary datacenter's federation state
|
|
|
|
// so we can just duplicate the new modify index.
|
|
|
|
config.PrimaryModifyIndex = idx
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert the federation state and update the index
|
2021-01-30 00:17:40 +00:00
|
|
|
if err := tx.Insert(tableFederationStates, config); err != nil {
|
2020-03-09 20:59:02 +00:00
|
|
|
return fmt.Errorf("failed inserting federation state: %s", err)
|
|
|
|
}
|
2021-03-08 18:02:41 +00:00
|
|
|
if err := tx.Insert(tableIndex, &IndexEntry{tableFederationStates, idx}); err != nil {
|
2020-03-09 20:59:02 +00:00
|
|
|
return fmt.Errorf("failed updating index: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FederationStateGet is called to get a federation state.
|
|
|
|
func (s *Store) FederationStateGet(ws memdb.WatchSet, datacenter string) (uint64, *structs.FederationState, error) {
|
|
|
|
tx := s.db.Txn(false)
|
|
|
|
defer tx.Abort()
|
2020-07-10 00:56:43 +00:00
|
|
|
return federationStateGetTxn(tx, ws, datacenter)
|
2020-03-09 20:59:02 +00:00
|
|
|
}
|
|
|
|
|
2020-09-03 23:38:03 +00:00
|
|
|
func federationStateGetTxn(tx ReadTxn, ws memdb.WatchSet, datacenter string) (uint64, *structs.FederationState, error) {
|
2020-03-09 20:59:02 +00:00
|
|
|
// Get the index
|
2021-01-30 00:17:40 +00:00
|
|
|
idx := maxIndexTxn(tx, tableFederationStates)
|
2020-03-09 20:59:02 +00:00
|
|
|
|
|
|
|
// Get the existing contents.
|
2021-01-30 00:17:40 +00:00
|
|
|
watchCh, existing, err := tx.FirstWatch(tableFederationStates, "id", datacenter)
|
2020-03-09 20:59:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, nil, fmt.Errorf("failed federation state lookup: %s", err)
|
|
|
|
}
|
|
|
|
ws.Add(watchCh)
|
|
|
|
|
|
|
|
if existing == nil {
|
|
|
|
return idx, nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
config, ok := existing.(*structs.FederationState)
|
|
|
|
if !ok {
|
|
|
|
return 0, nil, fmt.Errorf("federation state %q is an invalid type: %T", datacenter, config)
|
|
|
|
}
|
|
|
|
|
|
|
|
return idx, config, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FederationStateList is called to get all federation state objects.
|
|
|
|
func (s *Store) FederationStateList(ws memdb.WatchSet) (uint64, []*structs.FederationState, error) {
|
|
|
|
tx := s.db.Txn(false)
|
|
|
|
defer tx.Abort()
|
2020-07-10 00:56:43 +00:00
|
|
|
return federationStateListTxn(tx, ws)
|
2020-03-09 20:59:02 +00:00
|
|
|
}
|
|
|
|
|
2020-09-03 23:38:03 +00:00
|
|
|
func federationStateListTxn(tx ReadTxn, ws memdb.WatchSet) (uint64, []*structs.FederationState, error) {
|
2020-03-09 20:59:02 +00:00
|
|
|
// Get the index
|
2021-01-30 00:17:40 +00:00
|
|
|
idx := maxIndexTxn(tx, tableFederationStates)
|
2020-03-09 20:59:02 +00:00
|
|
|
|
2021-01-30 00:17:40 +00:00
|
|
|
iter, err := tx.Get(tableFederationStates, "id")
|
2020-03-09 20:59:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, nil, fmt.Errorf("failed federation state lookup: %s", err)
|
|
|
|
}
|
|
|
|
ws.Add(iter.WatchCh())
|
|
|
|
|
|
|
|
var results []*structs.FederationState
|
|
|
|
for v := iter.Next(); v != nil; v = iter.Next() {
|
|
|
|
results = append(results, v.(*structs.FederationState))
|
|
|
|
}
|
|
|
|
return idx, results, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Store) FederationStateDelete(idx uint64, datacenter string) error {
|
2020-03-19 13:11:20 +00:00
|
|
|
tx := s.db.WriteTxn(idx)
|
2020-03-09 20:59:02 +00:00
|
|
|
defer tx.Abort()
|
|
|
|
|
2020-07-10 00:56:43 +00:00
|
|
|
if err := federationStateDeleteTxn(tx, idx, datacenter); err != nil {
|
2020-03-09 20:59:02 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-06-02 20:34:56 +00:00
|
|
|
return tx.Commit()
|
2020-03-09 20:59:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Store) FederationStateBatchDelete(idx uint64, datacenters []string) error {
|
2020-03-19 13:11:20 +00:00
|
|
|
tx := s.db.WriteTxn(idx)
|
2020-03-09 20:59:02 +00:00
|
|
|
defer tx.Abort()
|
|
|
|
|
|
|
|
for _, datacenter := range datacenters {
|
2020-07-10 00:56:43 +00:00
|
|
|
if err := federationStateDeleteTxn(tx, idx, datacenter); err != nil {
|
2020-03-09 20:59:02 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-02 20:34:56 +00:00
|
|
|
return tx.Commit()
|
2020-03-09 20:59:02 +00:00
|
|
|
}
|
|
|
|
|
2021-03-08 22:41:10 +00:00
|
|
|
func federationStateDeleteTxn(tx WriteTxn, idx uint64, datacenter string) error {
|
2020-03-09 20:59:02 +00:00
|
|
|
// Try to retrieve the existing federation state.
|
2021-01-30 00:17:40 +00:00
|
|
|
existing, err := tx.First(tableFederationStates, "id", datacenter)
|
2020-03-09 20:59:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed federation state lookup: %s", err)
|
|
|
|
}
|
|
|
|
if existing == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete the federation state from the DB and update the index.
|
2021-01-30 00:17:40 +00:00
|
|
|
if err := tx.Delete(tableFederationStates, existing); err != nil {
|
2020-03-09 20:59:02 +00:00
|
|
|
return fmt.Errorf("failed removing federation state: %s", err)
|
|
|
|
}
|
2021-03-08 18:02:41 +00:00
|
|
|
if err := tx.Insert(tableIndex, &IndexEntry{tableFederationStates, idx}); err != nil {
|
2020-03-09 20:59:02 +00:00
|
|
|
return fmt.Errorf("failed updating index: %s", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|